In mathematics you don't understand things, you just get used to them.

标签 maths 下的文章

本来十分抗拒,但 GM 强制。

「ABC 183A」ReLU

Link.

略。

#include<cstdio>
int main()
{
    long long n;
    scanf("%lld",&n);
    printf("%lld\n",n>0?n:0);
    return 0;
}

「ABC 183B」Billiards

Link.

设答案坐标 $A(m,n)$,然后算出 $y_{AG}$ 解析式,再带 $x=S'_{x}$,$S'$ 是 $S$ 关于直线 $x=m$ 的对称点,得出来的 $y$ 要等于 $n$,然后列个方程解出来答案为 $\frac{S_{x}G_{y}+G_{x}S_{y}}{S_{y}+G_{y}}$。

#include<cstdio>
double sx,sy,gx,gy;
int main()
{
    scanf("%lf%lf%lf%lf",&sx,&sy,&gx,&gy);
    printf("%lf\n",(sx*gy+gx*sy)/(sy+gy));
    return 0;
}

「ABC 183C」Travel

Link.

全排列枚举算答案即可。

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> per;
int n,ans;
long long k,tim[20][20];
int main()
{
    scanf("%d%lld",&n,&k);
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n;++j)    scanf("%lld",&tim[i][j]);
    }
    per.resize(n+2);
    for(int i=1;i<=n;++i)    per[i]=i;
    per[n+1]=1;
    do
    {
        long long sum=0;
        for(int i=2;i<=n+1;++i)    sum+=tim[per[i-1]][per[i]];
        if(sum==k)    ++ans;
    }while(next_permutation(per.begin()+2,per.end()-1));
    printf("%d\n",ans);
    return 0;
}

「ABC 183D」Water Heater

Link.

前缀和。

#include<cstdio>
int n,s[200010],t[200010],p[200010],w;
long long dif[200010];
int main()
{
    scanf("%d%d",&n,&w);
    for(int i=1;i<=n;++i)    scanf("%d%d%d",&s[i],&t[i],&p[i]);
    for(int i=1;i<=n;++i)
    {
        dif[s[i]]+=p[i];
        dif[t[i]]-=p[i];
    }
    for(int i=1;i<=200000;++i)    dif[i]+=dif[i-1];
    for(int i=0;i<=200000;++i)
    {
        if(dif[i]>w)
        {
            printf("No\n");
            return 0;
        }
    }
    printf("Yes\n");
    return 0;
}

「ABC 183E」Water Heater

Link.

递推完了。

#include<cstdio>
const int mod=1e9+7;
long long ans;
int n,m,mp[2010][2010],row[2010],col[2010],dia[5010];
char str[2010];
int add(long long a,long long b)
{
    if(a+b>=mod)    return (a+b)%mod;
    else    return a+b;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",str+1);
        for(int j=1;j<=m;++j)
        {
            if(str[j]=='.')    mp[i][j]=0;
            else    mp[i][j]=1;
        }
    }
    int lay=2e3;
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            if(mp[i][j])
            {
                row[i]=0;
                col[j]=0;
                dia[i-j+lay]=0;
            }
            else
            {
                int tmp=add(add(row[i],col[j]),dia[i-j+lay]);
                if(i==1&&j==1)    ++tmp;
                row[i]=add(row[i],tmp);
                col[j]=add(col[j],tmp);
                dia[i-j+lay]=add(dia[i-j+lay],tmp);
                ans=tmp;
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

「ABC 183F」Confluence

Link.

并查集板。

#pragma GCC diagnostic error "-std=c++11"
#include<map>
#include<cstdio>
using namespace std;
map<int,int> mp[200010];
int n,m,fa[200010];
void makeset()
{
    for(int i=1;i<=n;++i)    fa[i]=i;
}
int findset(int x)
{
    if(x^fa[x])    fa[x]=findset(fa[x]);
    return fa[x];
}
void mergeset(int x,int y)
{
    x=findset(x);
    y=findset(y);
    if(x^y)
    {
        if(mp[x].size()>mp[y].size())
        {
            fa[y]=x;
            for(auto p:mp[y])    mp[x][p.first]+=p.second;
        }
        else
        {
            fa[x]=y;
            for(auto p:mp[x])    mp[y][p.first]+=p.second;
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    makeset();
    for(int i=1;i<=n;++i)
    {
        int x;
        scanf("%d",&x);
        mp[i][x]++;
    }
    while(m--)
    {
        int opt,opx,opy;
        scanf("%d%d%d",&opt,&opx,&opy);
        if(opt==1)    mergeset(opx,opy);
        else
        {
            int tmp=findset(opx);
            printf("%d\n",mp[tmp][opy]);
        }
    }
    return 0;
}

  • 曼哈顿距离 $\text{dist}(A,B)=|x_{A}-x_{B}|+|y_{A}-y_{B}|$ 可以拆成 $\max\{x_{A}-x_{B}+y_{A}-y_{B},x_{A}-x_{B}-y_{A}+y_{B},-x_{A}+x_{B}+y_{A}-y_{B},-x_{A}+x_{B}-y_{A}+y_{B} \}$。agc 034d
  • 走步数什么的构造题步数限制大约在 $\log$ 级别可考虑二进制拆分(倍增构造)。arc 103b
  • $x\bmod m$ 除了拆成 $x-\lfloor\frac{x}{m}\rfloor\times m$ 还可以拆成 $x-km,(k\in \mathbb{Z})$。arc 111b
  • 像什么 第 $i$ 个人对应第 $p_{i}$ 个物品,$p$ 是 $1,\cdots,n$ 的一个排列这种,直接连边。arc 111c / some abc d
  • 对应上一条,这种情况连边一定是一个这样 $i\rightarrow p_{i}\rightarrow p_{p_{i}}\rightarrow\cdots\rightarrow i$ 的环。arc 111c
  • 一个平面上有一堆点 $(x_{1},m),(x_{2},m),\cdots,(x_{n},m)$($x_{i}$ 单调递增),找一点 $(n,m)$ 使得 $\sum\text{dist}((x_{i},m),(n,m))$ 最小,则 $n$ 的取值范围是
    $[a_{\lfloor\frac{n+1}{2}\rfloor},a_{\lfloor\frac{n+2}{2}\rfloor}]$。cf 1486b
  • 字符串本质不同子串数:$\sum_{i=1}^{n}n-sa_{i}-ht_{i}+1=\sum_{i=1}^ni-ht_i$ / $\sum_{i=0}^{n-1}n-sa_i-ht_i=i+1-ht_i$。bzoj 4310
  • 把字符串反转后插入 SAM 后,两个原串的后缀在 parent tree 上的 LCA 是这两个后缀的 LCP。bzoj 3879
  • 对于树上/图上/序列上数某些个点中满足一些条件的点的个数(像 LCA、重心 之类的),考虑一个点能作为满足条件的点几次。plural
  • 我们记一个结点 $u$ 的重儿子为 $hb_{u}$,对于 $\text{subtree}(u)$,如果 $u$ 不是 $\text{subtree}(u)$ 的 centroid,那么 $\text{subtree}(u)$ 的 centroid 一定在 $\text{subtree}(hb(u))$ 里。csp 2019 centroid
  • 根据上一条,我们可以直接倍增找重心(注意只能从根开始找)。csp 2019 centroid
  • 值域比较小的关于值域的一些不等或等量关系的题可以考虑把值域放到指数构造生成函数。hk 2016 a+b problem
  • $i\times j=\binom{i+j}{2}-\binom{i}{2}-\binom{j}{2}$。loc 28870
  • 答案和阶乘相关的,并且模数长得很怪,比如 $998857459=461\times773\times2803$ 这种,当 $i\ge2803$ 时,$i!\bmod998857459=0$。loc 28853
  • BST 相关,序列有序,$[l,r]$ 对应 BST 上一棵子树,且其父结点一定是 $l-1$ 或 $r+1$,具体是哪个要看大小讨论。cf 1025d
  • 序列分段可以先考虑平方 DP 再优化。csp 2019 partition / ctsc 2012
  • 有多个关键字的 DS 题可以考虑离线按一个关键字排序。lg p3247
  • 找递推关系可以考虑差分。lg p6156
  • $low_{y}\leqslant dfn_{x}$ 意味着 $x,y$ 在一个环。plural
  • $a\&b+a\oplus b=a|b$; $a\&b+a|b=a+b$;$a+b=a\oplus b+2(a\&b)$。cf 1451e1
  • $fib(n+m)=fib(n+1)fib(m)+fib(n)fib(m-1)$。cf 446c
  • 棋盘向 下 / 右 走,很有些性质和 $n+m$ 有关,且移动一次 $i+j$ 加一,不会存在移动一次 $i+j$ 还是 $i+j$。同时在一条从右上往左下的对角线上的点 $i+j$ 相同。arc 120b
  • 操作类似于令 $a(i)\leftarrow a(i)-1,a(i+1)\leftarrow a(i+1)+1$ 然后交换 $a(i),a(i+1)$ 的,本质是保证下标,令 $A(i)=a(i)+i$ 可以使问题简化。arc 120c
  • 在 $n$ 个元素中连边如果行不通不妨考虑每个元素内部连边。sgu 101 / abc 209e
  • 有多个元素分别对答案贡献时,如果单个元素的贡献很好算,不妨考虑每个元素的贡献次数。many e.g. abc 209f
  • 出现 $a\mod p=b\mod p$ 时,等价于 $a-b\equiv0\pmod p$,对应到 $a_i\equiv a_{i+1}\equiv\dots\equiv a_j\pmod p$,就考虑差分。cf 1548b (cf 1549d)
  • 出现形似 $a\times b$ 为完全平方数的限制时,考虑消除平方因子,弱化成 $a=b$。cf 840c
  • $\gcd(a,b)=\gcd(a+kb,b)$。unknown
  • $\sum_i\sum_j[(i,j)=x]=\varphi(\lfloor\frac{N}{x}\rfloor)\times2-1$。
  • 当 $(a,b)=1$,$(a^i-b^i,a^j-b^j)=a^{(i,j)}-b^{(i,j)}$。
  • 如果一个函数 $f(x)$ 的 $k$ 阶差分是一个非零常数那么 $f(x)$ 一定是一个 $k$ 次多项式。
  • 若题目为序列的变化,考虑构造终止情况。
  • dp 刷表看看转移范围是否连续,可以看是否能整体 dp 维护。
  • 同一个图的 MST 每种权值的边的数量是一定的。cf 891c
  • 区间操作考虑差分(指题目中对各种序列的操作,不是那种数据结构题)。cf 1120d
  • 数列全为零等价于差分序列全为零。cf 1634f
  • 判定一个点是否在多边形内可由这个点以任意斜率拉出一条射线,看交点奇偶性。cf 375c
  • 数的出现次数的 mex 可以暴力求。cf 940f
  • 排列题可以放在逆排列里考虑,重新描述问题。wc 2022 rrads
  • 上上一条,不同出现次数的级别是根号,很多时候都可以考虑暴力。cf 1476g
  • 出现概率的和式考虑事件是否独立构造组合意义。cf 1523e
  • 和式 = 某个数,考虑看成某个数个 1 然后分配(精度思维)。hdu 7060

Description

Link.

给定 $n$ 组坐标。构造长度为 $m$ 的序列 $\{c_n\}$ 和 $n$ 组包含 LRUD 的路径,满足对于每一组坐标:

  • $c_i$ 表示第 $i$ 步「步长」。
  • 对于每个坐标,从 $(0,0)$ 开始走,共走 $m$ 步。第 $i$ 步可以让 $(x,y)$ 变成 $(x±c_i,y)$ 或 $(x,y±c_i)$ 。
  • 走完 $m$ 次之后,恰好走到这组坐标。
  • 要求 $m\leq 40,c_i\leq 10^{12}$ 。

Solution

好强的题啊。

先考虑无解的情况。

即是 $x_{i}+y_{i}$ 的奇偶性不同的情况为无解。

仔细看 $m$ 的限制疑似是 $\log(x+y)$ 级别的,考虑二进制拆分。

于是考虑 $\{2^{k}\}$ 可以凑出的坐标。

只考虑 1-dimension 的做法。

我们能够维护的地方就是 $\sum_{i=0}^{k}2^{i}=2^{k+1}-1$(这里算的是曼哈顿距离)。

那么这一定是个奇数,如果 $(x,y)$ 的曼哈顿距离是偶数就考虑换原点。

那么这就做完了。

full ver.

using i64 = long long;
using pii = std::pair<i64, i64>;

std::vector<int> sL;
std::vector<std::string> dR;
std::pair<int, int> as[MAXN];
int n, wax[4], way[4];
char trans[4];

int main () {
    std::ios::sync_with_stdio ( 0 ); std::cin.tie ( 0 ); std::cout.tie ( 0 );
    std::cin >> n; initial ();
    rep ( i, 1, n )    std::cin >> as[i].first >> as[i].second;
    rep ( i, 2, n ) if ( ( as[i].first + as[i].second + as[i - 1].first + as[i - 1].second ) & 1 )    return ( puts ( "-1" ), 0 );
    sL.push_back ( 1 );
    rep ( i, 1, 30 )    sL.push_back ( 1 << i );
    if ( ( ( as[1].first + as[1].second ) & 1 ) ^ 1 )    sL.push_back ( 1 );
    std::reverse ( sL.begin (), sL.end () );
    rep ( i, 1, n ) {
        dR.push_back ( std::string () );
        i64 curx = as[i].first, cury = as[i].second;
        if ( ( ( curx + cury ) & 1 ) ^ 1 )    dR[i - 1].push_back ( 'U' ), cury --;
        per ( j, 30, 0 ) rep ( k, 0, 3 ) {
            i64 nxtx = curx + ( i64 )wax[k] * ( ONE64 << j );
            i64 nxty = cury + ( i64 )way[k] * ( ONE64 << j );
            if ( std::abs ( nxtx ) + std::abs ( nxty ) < ( ONE64 << j ) ) {
                curx = nxtx, cury = nxty;
                dR[i - 1].push_back ( trans[k] );
                break;
            }
        }
    }
    std::cout << sL.size () << '\n';
    for ( int p : sL )    std::cout << p << ' ';
    std::cout << '\n';
    for ( std::string p : dR )    std::cout << p << '\n';
    return 0;
}

Desciption

Link.

给定一个值域在 $[1,x]$ 的长度为 $n$ 的序列(由随机数构成),求给定一组区间中的最小值的最大值的期望。

Solution

记:

$$ w=\max\{\min\{a_{l_{j}},a_{l_{j}+1},\cdots,a_{r_{j}}\}|j\in[1,q]\} $$

因为我们最后取的是 $\max$,不能直接用全概率公式,转化一下:

$$ E(w)=\sum_{i=0}^{\infty}P(w\ge i)=\sum_{i=0}^{\infty}1-P(w<i) $$

这意味着每一个被询问区间中的最小值都需 $<i$。也就是说,每一个区间至少需要一个 $<i$ 的数。

这对于每一个区间来说概率为 $\frac{i-1}{x}$。又因为区间可能出现相交,所以我们考虑用点去被包含于区间。

当然,一个区间包含另一个区间,这个区间肯定是没有用的。然后把区间按左右端点分别为第一、第二关键字排序。

枚举 $w$,设 $f_{i}$ 表示区间右端点在 $i$ 之前的所有区间满足条件的概率。

$$ f_{i}=\frac{w-1}{x}\times\sum_{j=0}^{i}f_{j}\times(1-\frac{w-1}{x})^{i-j-1} $$

#include <cstdio>

using i64 = long long;

const int MOD = 666623333;
const int MAXN = 2e3 + 5;

int n, x, q, ar[MAXN];
i64 f[MAXN][2], ff[MAXN][2];

void imax ( int& a, const int b ) { a = a < b ? b : a; }
int add ( const int a, const int b, const int p = MOD ) { return a + b < p ? a + b : ( a + b ) % p; }
int sub ( const int a, const int b, const int p = MOD ) { return a - b < 0 ? a - b + p : a - b; }
int mul ( const i64 a, const i64 b, const int p = MOD ) { return a * b % p; }
int cpow ( int bas, int idx = MOD - 2 ) {
    int res = 1;
    while ( idx ) {
        if ( idx & 1 )    res = mul ( res, bas );
        bas = mul ( bas, bas ), idx >>= 1;
    }
    return res % MOD;
}

int main () {
    scanf ( "%d%d%d", &n, &x, &q );
    for ( int i = 1, tmpl, tmpr; i <= q; ++ i )    scanf ( "%d%d", &tmpl, &tmpr ), imax ( ar[tmpr + 1], tmpl );
    for ( int i = 1; i <= n + 1; ++ i )    imax ( ar[i], ar[i - 1] );
    i64 ix = cpow ( x ), ans = 0;
    for ( int i = 1; i <= x; ++ i ) {
        i64 p = mul ( i - 1, ix ) % MOD, ip = cpow ( 1 - p ), s;
        ff[0][0] = ff[0][1] = 1;
        for ( int j = 1; j <= n; ++ j )    ff[j][0] = mul ( ff[j - 1][0], 1 - p ) % MOD, ff[j][1] = mul ( ff[j - 1][1], ip ) % MOD;
        f[0][0] = 0, f[0][1] = 1;
        for ( int j = 1; j <= n; ++ j ) {
            f[j][0] = mul ( mul ( p, sub ( f[j - 1][1], ar[j] ? f[ar[j] - 1][1] : 0 ) ) % MOD, ff[j - 1][0] ) % MOD;
            f[j][1] = add ( mul ( f[j][0], ff[j][1] ) % MOD, f[j - 1][1] ) % MOD;
        }
        s = 0;
        for ( int j = ar[n + 1]; j <= n; ++ j )    s = add ( s, mul ( f[j][0], ff[n - j][0] ) % MOD ) % MOD;
        ans = sub ( add ( ans, 1 ) % MOD, s );
    }
    printf ( "%lld\n", ans % MOD );
    return 0;
}

Description

Link.

定义一条链的价值为链上点权乘积除以节链上点数,求一条价值最小的链。

Solution

结论:答案链上最多包含一个 $2$(其余全为 $1$),并且不在链的两端点。

证明:我们问题分成两个 $\texttt{pass}$。

  • $\texttt{pass 1}$:$\forall u,s.t.x_{u}\ge2$。

答案显然为 $\min\{x_{u}\},u\in V$。

  • $\texttt{pass 2}$:$\exists E'\subset E,s.t.x_{u}=1,u\in E'\wedge x_{v}\ge2,v\in E\setminus E'$。
    • 我们设我们选出的链为大概这样的造型:

$$ 1\rightarrow1\rightarrow\cdots\rightarrow X\rightarrow1\rightarrow1\cdots $$

即一堆 $1$ 中夹了一个 $X$。

我们设 $X$ 左边有 $l$ 个节点,右边有 $r$ 个节点。

则价值为整条链 $\frac{X}{l+r+1}$,左边 $\frac{1}{l}$,右边 $\frac{1}{r}$。

为方便我们这里设 $l<r$。

那么左边的价值一定大于右边。

这里假设 $\frac{1}{r}>\frac{X}{l+r+1}$,则有 $X<\frac{l+1}{r}+1$,又 $r\ge l+1$,所以 $\frac{l+1}{r}\le1$。(反之可以证伪,懒得写了 QwQ)

所以有 $X\le2$。

又 $X\neq1$,所以 $X=2$。

    • 我们设我们选出的链为大概这样的造型:

$$ 1\rightarrow1\rightarrow\cdots\rightarrow X\rightarrow1\rightarrow\cdots\rightarrow1\rightarrow Y\rightarrow1\cdots $$

即一堆 $1$ 中夹了一个 $X$ 一个 $Y$。

这里我们可以把 $Y$ 以前当成 $\texttt{pass 2}$ 的第一个类型,设其共有 $N$ 个数。

那么假设我们加入 $Y$ 更优,即有 $\frac{XY}{N+1}<\frac{X}{N}$,则有 $NY<N+1$,由于 $Y\neq1$,所以加入 $Y$ 是更劣的。

后面的同理就可以推广了。

于是得证 QwQ。

然后我们就可以 DP 了。

设 $f_{u,0/1}$ 表示节点 $u$ 权值为的情况下最优答案。

转移就分类讨论一下:

  • $x_{u}=1$

$$ \begin{cases} f_{u,0}=\max\{f_{v,0}\}+1 \\ f_{u,1}=\max\{f_{v,1}\}+1 \end{cases} $$

  • $x_{u}=2$

$$ f_{u,1}=\max\{f_{v,0}\}+1 $$

答案也需要分类讨论(这里设 $x,y\in\text{son}(u)$):

  • $x_{u}=1$

答案为 $\frac{1}{\max\{f_{x,0}+f_{y,0}+1\}}$,以及 $\frac{2}{\max\{f_{x,0}+f_{y,1}\}+1}$。

  • $x_{u}=2$

答案为 $\frac{2}{\max\{f_{x,0}+f_{y,0}+1\}}$。

用四个变量维护最大、次大的 $f_{0},f_{1}$ 即可。

#include <cstdio>

const int MAXN = 1e6 + 5;

int rint () {
    int x = 0, f = 1; char c = getchar ();
    for ( ; c < '0' || c > '9'; c = getchar () )    f = c == '-' ? -f : f;
    for ( ; c >= '0' && c <= '9'; c = getchar () )    x = ( x << 3 ) + ( x << 1 ) + ( c & 15 );
    return x * f;
}

template<typename _T>
void wint ( _T x ) {
    if ( x < 0 )    putchar ( '-' ), x = ~ x + 1;
    if ( x > 9 )    wint ( x / 10 );
    putchar ( x % 10 ^ '0' );
}

template<typename _T> _T MIN ( const _T x, const _T y ) { return x > y ? y : x; }

struct starS {
    int to, nx;
    starS ( int T = 0, int N = 0 ) { to = T, nx = N; }
} as[MAXN * 2];

int n, cnt, Up = 1e9, Dn = 1, mnMg = 1e9, a[MAXN], f[MAXN][2], bgin[MAXN];

void pushEdge ( const int u, const int v ) { as[++ cnt] = starS ( v, bgin[u] ); bgin[u] = cnt; }

void checkUpt ( const int x, const int y ) { if ( Up * y > Dn * x )    Up = x, Dn = y; }

void dfs ( const int u, const int lst ) {
    int mx0 = 0, se0 = 0, mx1 = 0, se1 = 0;
    for ( int i = bgin[u]; i; i = as[i].nx ) {
        int v = as[i].to;
        if ( v == lst )    continue;
        dfs ( v, u );
        if ( f[v][0] > f[mx0][0] )    se0 = mx0, mx0 = v;
        else if ( f[v][0] > f[se0][0] )    se0 = v;
        if ( f[v][1] > f[mx1][1] )    se1 = mx1, mx1 = v;
        else if ( f[v][1] > f[se1][1] )    se1 = v;
    }
    if ( a[u] == 1 ) {
        f[u][0] = f[mx0][0] + 1;
        checkUpt ( 1, f[mx0][0] + f[se0][0] + 1 );
        if ( ! mx1 )    return;
        f[u][1] = f[mx1][1] + 1;
        if ( mx0 != mx1 )    checkUpt ( 2, f[mx0][0] + f[mx1][1] + 1 );
        else {
            checkUpt ( 2, f[se0][0] + f[mx1][1] + 1 );
            if ( se1 )    checkUpt ( 2, f[mx0][0] + f[se1][1] + 1 );
        }
    }
    else if ( a[u] == 2 )    f[u][1] = f[mx0][0] + 1, checkUpt ( 2, f[mx0][0] + f[se0][0] + 1 );
}

int main () {
    n = rint ();
    for ( int i = 1, u, v; i < n; ++ i ) {
        u = rint (), v = rint ();
        pushEdge ( u, v ), pushEdge ( v, u );
    }
    for ( int i = 1; i <= n; ++ i )    a[i] = rint (), mnMg = MIN ( mnMg, a[i] );
    if ( mnMg > 1 )    wint ( mnMg ), putchar ( '/' ), wint ( 1 ), putchar ( '\n' );
    else    dfs ( 1, 0 ), wint ( Up ), putchar ( '/' ), wint ( Dn ), putchar ( '\n' );
    return 0;
}