「十二省联考 2019」希望
Solution
- 题意简述:选出 k k k 个树上连通块,使得存在一个点 u u u 满足:
1. u u u 在这 k k k 个连通块的交集之中。
2.对于这 k k k 个连通块中的任意一点 v v v,都有: d i s t ( v , u ) ≤ L dist(v,u)≤L dist(v,u)≤L。
1.容斥
- 我们钦定根节点为1。记 u u u 的父亲为 f a [ u ] fa[u] fa[u] 。记所有合法方案中,满足连通块交集包含点 u u u 的方案数为 A ( u ) A(u) A(u),满足连通块交集包含边 ( u , f a [ u ] ) (u,fa[u]) (u,fa[u]) 的方案数为 B ( u ) B(u) B(u),那么 ∑ i = 1 n A ( i ) − ∑ i = 2 n B ( i ) \sum_{i=1}^{n}A(i)-\sum_{i=2}^{n}B(i) ∑i=1nA(i)−∑i=2nB(i) 就是答案。
- 考虑这样算为什么是对的:
- 设一个合法方案中,连通块的交集为 S S S。对于 S S S 中的每个点 u u u, A ( u ) A(u) A(u) 中都包含此方案。对于 S S S 中的每条边 ( v , f a [ v ] ) (v,fa[v]) (v,fa[v]), B ( v ) B(v) B(v) 中都包含此方案。众所周知,树上连通块有一个绝妙的性质:点数 = = = 边数 + 1 +1 +1。那么 u u u 的个数会比 v v v 的个数多 1 1 1,也就是说此合法方案正好被统计一次。由此可得,每个合法方案都正好被统计一次,那么这样算就是对的。
2.朴素dp
记 f [ u ] [ i ] f[u][i] f[u][i] 表示满足以下条件的连通块 T T T 的个数 + 1 +1 +1:
1. T T T 中必须包含 u u u。
2. T T T 中只能包含 u u u 子树内的点。
初值: f [ u ] [ 0 ] = 2 f[u][0]=2 f[u][0]=2。
d p dp dp式: f [ u ] [ i ] = ∏ v ∈ c h i l d [ u ] f [ v ] [ i − 1 ] + 1 f[u][i]=\prod_{v∈child[u]}f[v][i-1]+1 f[u][i]=∏v∈child[u]f[v][i−1]+1。记 g [ u ] [ i ] g[u][i] g[u][i] 表示满足以下条件的连通块 T T T 的个数:
1. T T T 中必须包含 u u u。
2. T T T 中只能包含 u u u 和 u u u 子树外的点。
初值: g [ u ] [ 0 ] = 1 g[u][0]=1 g[u][0]=1。
d p dp dp式: g [ v ] [ i ] = g [ u ] [ i − 1 ] ∗ ∏ x ∈ c h i l d [ u ] 且 x ! = v f [ x ] [ i − 2 ] + 1 g[v][i]=g[u][i-1]*\prod_{x∈child[u]且x!=v}f[x][i-2]+1 g[v][i]=g[u][i−1]∗∏x∈child[u]且x!=vf[x][i−2]+1。
特殊地, i = 1 i=1 i=1 时不用乘 ∏ x ∈ c h i l d [ u ] 且 x ! = v f [ x ] [ i − 2 ] \prod_{x∈child[u]且x!=v}f[x][i-2] ∏x∈child[u]且x!=vf[x][i−2]。A ( u ) = [ ( f [ u ] [ L ] − 1 ) ∗ ( g [ u ] [ L ] ) ] k A(u)=[(f[u][L]-1)*(g[u][L])]^k A(u)=[(f[u][L]−1)∗(g[u][L])]k
B ( u ) = [ ( f [ u ] [ L − 1 ] − 1 ) ∗ ( g [ u ] [ L ] − 1 ) ] k B(u)=[(f[u][L-1]-1)*(g[u][L]-1)]^k B(u)=[(f[u][L−1]−1)∗(g[u][L]−1)]k
3.长链剖分优化 f f f
- 考虑如果 v v v 是 u u u 的轻儿子怎么转移:
- 记 m x [ v ] mx[v] mx[v] 为点 v v v 子树里深度最大的点到 v v v 的距离。
- 转移 f [ u ] [ i ] = f [ v ] [ i − 1 ] + 1 f[u][i]=f[v][i-1]+1 f[u][i]=f[v][i−1]+1 的时候, i i i 最大会到 m x [ u ] mx[u] mx[u]。但是,为了保证时间复杂度为 ∑ \sum ∑ 链长, i i i 是不可以枚举到 m x [ u ] mx[u] mx[u] 的。
- 怎么办呢?我们发现对于任意 i − 1 > m x [ v ] i-1>mx[v] i−1>mx[v],都有 f [ v ] [ i − 1 ] = f [ v ] [ m x [ v ] ] f[v][i-1]=f[v][mx[v]] f[v][i−1]=f[v][mx[v]]。
- 那么我们不用枚举到 m x [ u ] mx[u] mx[u]。我们只要枚举 i i i 到 m x [ v ] + 1 mx[v]+1 mx[v]+1 就可以了。
- 至于 v v v 对 f [ u ] [ m x [ v ] + 2... m x [ u ] ] f[u][mx[v]+2...mx[u]] f[u][mx[v]+2...mx[u]] 的贡献,我们考虑打一个标记 m u l [ u ] mul[u] mul[u],表示 f [ u ] [ 0... m x [ u ] ] f[u][0...mx[u]] f[u][0...mx[u]] 都乘上 m u l [ u ] mul[u] mul[u]。
- 显然要: m u l [ u ] ∗ = f [ v ] [ m x [ v ] ] mul[u]*=f[v][mx[v]] mul[u]∗=f[v][mx[v]]。
- 然后对于 f [ u ] [ 0... m x [ v ] + 1 ] f[u][0...mx[v]+1] f[u][0...mx[v]+1],暴力乘上 f [ v ] [ m x [ v ] ] f[v][mx[v]] f[v][mx[v]] 的逆元即可。这样时间复杂度就是对的了。
- 还有注意一点, f [ v ] [ m x [ v ] ] f[v][mx[v]] f[v][mx[v]] 的逆元不可以用快速幂计算(太慢),要提前 O ( n ) O(n) O(n) 预处理所有的 f [ v ] [ m x [ v ] ] f[v][mx[v]] f[v][mx[v]],然后 O ( n ) O(n) O(n) 求所有 f [ v ] [ m x [ v ] ] f[v][mx[v]] f[v][mx[v]] 的逆元。不会线性求逆元的左转 Luogu5431。
- 然后不是递推式的末尾有个 + 1 +1 +1 吗, 那么再记个 a d d [ u ] add[u] add[u]。现在 f [ u ] [ i ] f[u][i] f[u][i] 的真实值就是 f [ u ] [ i ] ∗ m u l [ u ] + a d d [ u ] f[u][i]*mul[u]+add[u] f[u][i]∗mul[u]+add[u] 了。
- 然后为了保证 f [ u ] [ i ] ∗ m u l [ u ] + a d d [ u ] f[u][i]*mul[u]+add[u] f[u][i]∗mul[u]+add[u] 是 f [ u ] [ i ] f[u][i] f[u][i] 的真实值,我们递推的时候不能直接 f [ u ] [ i ] ∗ = f [ v ] [ i − 1 ] f[u][i]*=f[v][i-1] f[u][i]∗=f[v][i−1],而是要这样:
- 记 a s k 0 ( u , i ) ask0(u,i) ask0(u,i) 表示 f [ u ] [ i ] f[u][i] f[u][i] 真实值。
- 记 a s k 1 ( u , r e s ) ask1(u,res) ask1(u,res) 表示已知 f [ u ] [ i ] f[u][i] f[u][i] 的真实值为 r e s res res,求 f [ u ] [ i ] f[u][i] f[u][i]。
- 显然: a s k 1 ( u , r e s ) = ( r e s − a d d [ u ] ) ∗ i m u l [ u ] ask1(u,res)=(res-add[u])*imul[u] ask1(u,res)=(res−add[u])∗imul[u],其中 i m u l [ u ] imul[u] imul[u] 是 m u l [ u ] mul[u] mul[u] 的逆元,维护 m u l [ u ] mul[u] mul[u] 的同时也要维护 i m u l [ u ] imul[u] imul[u]。
- 转移: f [ u ] [ i ] = a s k 1 ( a s k 0 ( u , i ) ∗ a s k 0 ( v , i − 1 ) , i ) f[u][i]=ask1(ask0(u,i)*ask0(v,i-1),i) f[u][i]=ask1(ask0(u,i)∗ask0(v,i−1),i)。
- 然后暴力乘逆元的时候也要类似地利用 a s k 1 , a s k 0 ask1,ask0 ask1,ask0。
- 如果 v v v 是重儿子,直接把 f [ v ] , i m u l [ v ] , m u l [ v ] , a d d [ v ] f[v],imul[v],mul[v],add[v] f[v],imul[v],mul[v],add[v] 都传给 u u u 就行了。
- 但是,如果 f [ v ] [ m x [ v ] ] m o d    998244353 = 0 f[v][mx[v]]\mod 998244353 = 0 f[v][mx[v]]mod998244353=0 呢?
- 那么此时相当于把 f [ u ] [ m x [ v ] + 1... m x [ u ] ] f[u][mx[v]+1...mx[u]] f[u][mx[v]+1...mx[u]] 都赋值成 0 0 0。
- 我们考虑多记两个标记 l i m [ u ] , z e r o [ u ] lim[u],zero[u] lim[u],zero[u],然后定义:
若 i < l i m [ u ] , a s k 0 ( u , i ) = f [ u ] [ i ] ∗ m u l [ u ] + a d d [ u ] i<lim[u],ask0(u,i)=f[u][i]*mul[u]+add[u] i<lim[u],ask0(u,i)=f[u][i]∗mul[u]+add[u],
否则 a s k 0 ( u , i ) = z e r o [ u ] ∗ m u l [ u ] + a d d [ u ] 。 ask0(u,i)=zero[u]*mul[u]+add[u]。 ask0(u,i)=zero[u]∗mul[u]+add[u]。 - 那么如果 f [ v ] [ m x [ v ] ] = 0 f[v][mx[v]]=0 f[v][mx[v]]=0,令 l i m [ u ] = m x [ v ] + 2 , z e r o [ u ] = a s k 1 ( u , 0 ) lim[u]=mx[v]+2,zero[u]=ask1(u,0) lim[u]=mx[v]+2,zero[u]=ask1(u,0)。
- 转移轻儿子 v v v,枚举到 i i i 的时候,如果发现 l i m [ u ] = = i lim[u]==i lim[u]==i,说明此时 l i m [ u ] lim[u] lim[u] 的值需要增加。那么 l i m [ u ] + + , f [ u ] [ i ] = z e r o [ u ] lim[u]++,f[u][i]=zero[u] lim[u]++,f[u][i]=zero[u] 即可。
长链剖分优化f-参考程序
int *f[e], tmp1[e * 10], *it = tmp1 + 2;namespace sf
{int mul[e], imul[e], add[e], lim[e], zero[e];inline void init(int u){f[u] = it;it += (mx[u] + 5) * 2;}inline int ask0(int u, int i){i = min(i, mx[u]);if (lim[u] <= i) return ((ll)mul[u] * zero[u] + add[u]) % mod;return ((ll)mul[u] * f[u][i] + add[u]) % mod;}inline int ask1(int u, int res){ return (ll)sub(res, add[u]) * imul[u] % mod;}inline void dfs3(int u){if (!son[u]){mul[u] = imul[u] = 1; add[u] = 2; lim[u] = n + 1;f1[u] = sub(ask0(u, L), 1); f2[u] = sub(ask0(u, L - 1), 1);return;}else{f[son[u]] = f[u] + 1; dfs3(son[u]); add[u] = add[son[u]]; mul[u] = mul[son[u]]; imul[u] = imul[son[u]]; lim[u] = lim[son[u]] + 1;zero[u] = zero[son[u]]; f[u][0] = ask1(u, 1);}for (auto v : adj[u]){if (v == son[u]) continue;init(v); dfs3(v);for (int i = 0; i <= mx[v] + 1; i++){if (lim[u] == i) f[u][lim[u]++] = zero[u];f[u][i] = ask1(u, (ll)ask0(u, i) * (i ? ask0(v, i - 1) : 1) % mod);} if (!p[v]){lim[u] = mx[v] + 2; zero[u] = ask1(u, 0);}else{mul[u] = (ll)mul[u] * p[v] % mod; add[u] = (ll)add[u] * p[v] % mod;imul[u] = (ll)imul[u] * inv[v] % mod;for (int i = 0; i <= mx[v] + 1; i++)f[u][i] = ask1(u, (ll)ask0(u, i) * inv[v] % mod);}}add[u] = plu(add[u], 1); f1[u] = sub(ask0(u, L), 1); f2[u] = sub(ask0(u, L - 1), 1);// f1[u]=f[u][L]真实值-1,f2[u]=f[u][L-1]真实值-1}
}
4.长链剖分优化g
回顾 g g g 的 d p dp dp 式:
g [ v ] [ i ] = g [ u ] [ i − 1 ] ∗ ∏ x ∈ c h i l d [ u ] 且 x ! = v f [ x ] [ i − 2 ] + 1 g[v][i]=g[u][i-1]*\prod_{x∈child[u]且x!=v}f[x][i-2]+1 g[v][i]=g[u][i−1]∗∏x∈child[u]且x!=vf[x][i−2]+1。对于 g [ v ] [ i ] = g [ u ] [ i − 1 ] g[v][i]=g[u][i-1] g[v][i]=g[u][i−1] 这一部分,直接把 g g g 传给重儿子,然后轻儿子暴力转移即可。
注意轻儿子只要转移到 g [ v ] [ m a x ( L − m x [ v ] , 0 ) . . . L ] g[v][max(L-mx[v],0)...L] g[v][max(L−mx[v],0)...L]。
然后 ∏ f [ x ] [ i − 2 ] \prod f[x][i-2] ∏f[x][i−2] 怎么办呢?
对于重儿子依然可以暴力计算。和 f f f 一样,也要记 l i m , a d d , i m u l , m u l , z e r o lim,add,imul,mul,zero lim,add,imul,mul,zero。
对于轻儿子呢?我们发现它等于 f [ u ] [ i − 1 ] − 1 f [ v ] [ i − 2 ] \frac{f[u][i-1]-1}{f[v][i-2]} f[v][i−2]f[u][i−1]−1,但要是 f [ v ] [ i − 2 ] m o d    998244353 = 0 f[v][i-2] \mod 998244353 =0 f[v][i−2]mod998244353=0 呢?
所以我们只能把它拆成一段前缀和一段后缀相乘的形式。
- 先考虑前缀怎么办:
- 我们把轻儿子按 m x [ v ] mx[v] mx[v] 升序排序。然后记 b [ i ] b[i] b[i] 表示: ∏ x 的 d f s 序 ≤ v 且 x ∈ c h i l d [ u ] f [ x ] [ i ] \prod_{x的dfs序≤v且x∈child[u]}f[x][i] x的dfs序≤v且x∈child[u]∏f[x][i]
- 也就是在枚举到 v v v 的时候把 f [ v ] [ 0... m x [ v ] ] f[v][0...mx[v]] f[v][0...mx[v]] 计入 b [ 0... m x [ v ] ] b[0...mx[v]] b[0...mx[v]] 就行了。
- 然后对于 b [ m x [ v ] + 1... ∞ ] b[mx[v]+1...∞] b[mx[v]+1...∞],显然 v v v 对它们的贡献相同,那么额外记一个值就行了。
- 大概就是这样:
int now = 0, tot = 1;
for (int i = 0; i <= mx[v]; i++)
if (i > now) b[i] = (ll)tot * sf::ask0(v, i) % mod;
else b[i] = (ll)b[i] * sf::ask0(v, i) % mod;
now = mx[v];
tot = (ll)tot * sf::ask0(v, mx[v]) % mod; //额外记一个 tot
- 我们发现计算 f [ u ] [ i ] f[u][i] f[u][i] 的时候, f [ u ] [ i ] f[u][i] f[u][i] 的真实值要不断乘上 f [ v ] [ i − 1 ] f[v][i-1] f[v][i−1]。也就是说,枚举到轻儿子 v v v 的时候, f [ u ] [ i ] f[u][i] f[u][i] 的真实值是 ∏ x 的 d f s 序 ≤ v 且 x ∈ c h i l d [ u ] f [ x ] [ i − 1 ] \prod_{x的dfs序≤v且x∈child[u]}f[x][i-1] x的dfs序≤v且x∈child[u]∏f[x][i−1]
- 这就是一段前缀的形式了。如果我们计算 g g g 的时候,把子节点的 d f s dfs dfs 顺序全部反过来,那它就是一段后缀的形式了。
- 那就是说,在计算 f f f 的时候,把轻儿子按 m x [ v ] mx[v] mx[v] 降序排序,然后计算 g g g 的时候反过来就行了。
- 但是我们现在并没有存下对于每个 v v v,上式的值。
- 那么我们要做的就是:假设计算 f f f 的时候,儿子的 d f s dfs dfs 顺序为: v 1 , v 2 , v 3 , . . . , v m v_1,v_2,v_3,...,v_m v1,v2,v3,...,vm。
- 然后现在反过来,枚举到 v m v_m vm 的时候,我们要算出: ∏ j = 1 m − 1 f [ v j ] [ i ] \prod_{j=1}^{m-1}f[v_j][i] ∏j=1m−1f[vj][i],枚举到 v m − 1 v_{m-1} vm−1 的时候,我们要算出: ∏ j = 1 m − 2 f [ v j ] [ i ] \prod_{j=1}^{m-2}f[v_j][i] ∏j=1m−2f[vj][i]……
- 然后把算出的这个和 b b b 相乘就可以得出 ∏ f [ x ] [ i − 2 ] \prod f[x][i-2] ∏f[x][i−2] 了。
- 具体地,我们可以在计算 f f f 的时候,枚举到每个轻儿子 v j v_j vj 的时候,都记一下在计算 v j v_j vj 的贡献之前, f [ u ] [ i ] f[u][i] f[u][i] 的真实值。也就是对于每个 v j v_j vj 都记下 ∏ k = 1 j − 1 f [ v ] [ i − 1 ] \prod_{k=1}^{j-1}f[v][i-1] ∏k=1j−1f[v][i−1]。
- 但是不能直接把真实值记下来。因为计算 f f f 的时候转移的是 f [ v ] [ 0... m x [ v ] + 1 ] f[v][0...mx[v]+1] f[v][0...mx[v]+1]。而计算 g g g 的时候要算的是 g [ v ] [ m a x ( L − m x [ v ] , 0 ) . . . L ] g[v][max(L-mx[v],0)...L] g[v][max(L−mx[v],0)...L]。
- 所以我们在计算 f f f ,枚举到轻儿子 v v v 的时候,如果 f [ u ] [ i ] , l i m [ u ] , z e r o [ u ] , a d d [ u ] , m u l [ u ] , i m u l [ u ] f[u][i],lim[u],zero[u],add[u],mul[u],imul[u] f[u][i],lim[u],zero[u],add[u],mul[u],imul[u] 中任意一个的值改变了,都要把改变之前的值记下来。
- 最后 f [ u ] [ i ] f[u][i] f[u][i] 的值有 + 1 +1 +1,看作是枚举到最后一个轻儿子时做的修改。
- 具体实现时可以对每个 v v v 开一个栈 ( l i s t list list实现),计算 f f f 的时候记录下修改的地址和值,然后计算 g g g 的时候,按从栈顶到栈底的顺序还原这些元素的值。然后利用 s f : : a s k 0 sf::ask0 sf::ask0 就能知道 ∏ k = 1 j − 1 f [ v ] [ i − 1 ] \prod_{k=1}^{j-1}f[v][i-1] ∏k=1j−1f[v][i−1] 的真实值了。
Code
#include <bits/stdc++.h>using namespace std;#define ll long long
#define pb push_backtemplate <class t>
inline void read(t & res)
{char ch;while (ch = getchar(), !isdigit(ch));res = ch ^ 48;while (ch = getchar(), isdigit(ch))res = res * 10 + (ch ^ 48);
}const int e = 1e6 + 5, mod = 998244353;
vector<int>adj[e], h[e];
int *f[e], tmp1[e * 10], *it = tmp1 + 2, ans, n, L, fa[e], k, mx[e], b[e], ret[e];
int son[e], p[e], inv[e], pre[e], suf[e], f1[e], f2[e], *g[e], tmp2[e * 10];
struct point
{int x, y;
}a[e]; inline int plu(int x, int y)
{x += y;if (x >= mod) x -= mod;return x;
}inline int sub(int x, int y)
{x -= y;if (x < 0) x += mod;return x;
}inline int ksm(int x, int y)
{int res = 1;while (y){if (y & 1) res = (ll)res * x % mod;y >>= 1;x = (ll)x * x % mod;}return res;
}inline void dfs0(int u, int pa)
{fa[u] = pa;for (auto v : h[u]){if (v == pa) continue;dfs0(v, u);}
}inline bool cmp(int x, int y)
{return mx[x] > mx[y];
}inline void dfs1(int u)
{for (auto v : adj[u]){dfs1(v);mx[u] = max(mx[u], mx[v] + 1);if (mx[v] > mx[son[u]]) son[u] = v;}sort(adj[u].begin(), adj[u].end(), cmp);
}inline void dfs2(int u)
{p[u] = 1;for (auto v : adj[u]){dfs2(v);p[u] = (ll)p[u] * p[v] % mod;}p[u] = plu(p[u], 1);
}inline void prepare() // 线性求逆元
{int i; pre[0] = 1;for (i = 1; i <= n; i++) if (p[i]) pre[i] = (ll)pre[i - 1] * p[i] % mod;else pre[i] = pre[i - 1];suf[n] = ksm(pre[n], mod - 2);suf[n + 1] = 0;for (i = n - 1; i >= 0; i--) if (p[i + 1]) suf[i] = (ll)suf[i + 1] * p[i + 1] % mod;else suf[i] = suf[i + 1];for (i = 1; i <= n; i++) inv[i] = (ll)pre[i - 1] * suf[i] % mod;
}struct work
{struct node{int *w, v; // 记录修改的地址和修改之前的值};list<node>q;inline void ins(int &x){q.pb((node){&x, x});}inline void regain(){while (!q.empty()) *(q.back().w) = q.back().v, q.pop_back(); // 还原 }
}q[e];namespace sf
{int mul[e], imul[e], add[e], lim[e], zero[e];inline void init(int u) // 地址分配{f[u] = it;it += (mx[u] + 5) * 2;}inline int ask0(int u, int i){i = min(i, mx[u]);if (lim[u] <= i) return ((ll)mul[u] * zero[u] + add[u]) % mod;return ((ll)mul[u] * f[u][i] + add[u]) % mod;}inline int ask1(int u, int res){ return (ll)sub(res, add[u]) * imul[u] % mod;}inline void dfs3(int u){if (!son[u]){mul[u] = imul[u] = 1; add[u] = 2; lim[u] = n + 1;f1[u] = sub(ask0(u, L), 1); f2[u] = sub(ask0(u, L - 1), 1);return;}else{f[son[u]] = f[u] + 1; dfs3(son[u]); add[u] = add[son[u]]; mul[u] = mul[son[u]]; imul[u] = imul[son[u]]; lim[u] = lim[son[u]] + 1;zero[u] = zero[son[u]]; f[u][0] = ask1(u, 1);}int lst = 0;for (auto v : adj[u]){if (v == son[u]) continue;init(v); dfs3(v); lst = v;for (int i = 0; i <= mx[v] + 1; i++){if (lim[u] == i) q[v].ins(lim[u]), q[v].ins(f[u][i]), f[u][lim[u]++] = zero[u];q[v].ins(f[u][i]);f[u][i] = ask1(u, (ll)ask0(u, i) * (i ? ask0(v, i - 1) : 1) % mod);} if (!p[v]){q[v].ins(lim[u]); q[v].ins(zero[u]);lim[u] = mx[v] + 2; zero[u] = ask1(u, 0);}else{q[v].ins(mul[u]); q[v].ins(add[u]); q[v].ins(imul[u]);mul[u] = (ll)mul[u] * p[v] % mod; add[u] = (ll)add[u] * p[v] % mod;imul[u] = (ll)imul[u] * inv[v] % mod;for (int i = 0; i <= mx[v] + 1; i++)q[v].ins(f[u][i]), f[u][i] = ask1(u, (ll)ask0(u, i) * inv[v] % mod);}}if (lst) q[lst].ins(add[u]);add[u] = plu(add[u], 1); f1[u] = sub(ask0(u, L), 1); f2[u] = sub(ask0(u, L - 1), 1);}
}namespace sg
{int mul[e], imul[e], add[e], lim[e], zero[e];inline void init(int u){it += mx[u] + 5;g[u] = it - max(L - mx[u], 0);it += mx[u] + 5;}inline int ask0(int u, int i){if (lim[u] <= i) return ((ll)mul[u] * zero[u] + add[u]) % mod;else return ((ll)mul[u] * g[u][i] + add[u]) % mod;}inline int ask1(int u, int res){ return (ll)sub(res, add[u]) * imul[u] % mod;}inline void dfs4(int u){int gu = ask0(u, L), tot = 1, now = 0, x = son[u];ans = plu(ans, ksm((ll)f1[u] * gu % mod, k));ret[u] = ask0(u, L);if (u != 1) {gu = sub(gu, 1);ans = sub(ans, ksm((ll)f2[u] * gu % mod, k));}if (!x) return; b[0] = 1; reverse(adj[u].begin(), adj[u].end());for (auto v : adj[u]){if (v == x) continue;q[v].regain(); // 还原init(v); mul[v] = imul[v] = 1; lim[v] = n + 1;for (int i = max(0, L - mx[v]); i <= L; i++){g[v][i] = (ll)(i ? ask0(u, i - 1): 1) * (i - 2 > now ? tot : i >= 2 ? b[i - 2] : 1) % mod * (i ? sf::ask0(u, i - 1) : 1) % mod; if (i) g[v][i] = plu(g[v][i], 1);}for (int i = 0; i <= mx[v]; i++)if (i > now) b[i] = (ll)tot * sf::ask0(v, i) % mod;else b[i] = (ll)b[i] * sf::ask0(v, i) % mod;now = mx[v]; tot = (ll)tot * sf::ask0(v, mx[v]) % mod;}add[x] = add[u]; mul[x] = mul[u]; imul[x] = imul[u];lim[x] = lim[u] + 1; zero[x] = zero[u]; g[x] = g[u] - 1;int st = max(L - mx[x], 0);for (auto v : adj[u]){if (v == x) continue; int ed = min(mx[v] + 2, L);for (int i = st; i <= ed; i++){if (lim[x] == i) g[x][lim[x]++] = zero[x];g[x][i] = ask1(x, (ll)ask0(x, i) * (i >= 2 ? sf::ask0(v, i - 2) : 1) % mod);}if (L <= mx[v] + 2) continue; if (!p[v]) lim[x] = max(mx[v] + 3, L - mx[x]), zero[x] = ask1(x, 0);else{mul[x] = (ll)mul[x] * p[v] % mod; add[x] = (ll)add[x] * p[v] % mod;imul[x] = (ll)imul[x] * inv[v] % mod;for (int i = st; i <= ed; i++)g[x][i] = ask1(x, (ll)ask0(x, i) * inv[v] % mod);}}add[x] = plu(add[x], 1); if (L - mx[x] <= 0) g[x][0] = ask1(x, 1);for (auto v : adj[u]) dfs4(v);} inline void begin(){mul[1] = imul[1] = add[1] = 1;lim[1] = n + 1; init(1);}
}int main()
{int i, x, y, j;read(n); read(L); read(k); mx[0] = -1;for (i = 1; i < n; i++) {read(x); read(y);a[i].x = x; a[i].y = y;h[x].pb(y); h[y].pb(x);}if (L == 0){cout << n << endl;return 0;}dfs0(1, 0);for (i = 1; i < n; i++){x = a[i].x; y = a[i].y;if (fa[y] == x) adj[x].pb(y);else adj[y].pb(x);}dfs1(1); dfs2(1); prepare(); sf::init(1); sf::dfs3(1);it = tmp2 + 2;sg::begin(); sg::dfs4(1);cout << ans << endl;fclose(stdin);fclose(stdout);return 0;
}
「十二省联考 2019」希望相关推荐
- 「十二省联考 2019」皮配——dp
题目 [题目描述] #### 题目背景 一年一度的综艺节目<中国好码农>又开始了.本季度,好码农由 Yazid.Zayid.小 R.大 R 四位梦想导师坐镇,他们都将组建自己的梦想战队,并 ...
- LOJ 3049: 洛谷 P5284: 「十二省联考 2019」字符串问题
题目传送门:LOJ #3049. 题意简述: 给定一个长度为 \(n\) 的母串 \(S\). 有 \(n_a\) 个 A 类串,都是 \(S\) 的子串,以区间的形式给出. 有 \(n_b\) 个 ...
- 【十二省联考2019】希望【点边容斥】【换根dp】【长链剖分】【线性数据结构】【回退数据结构】【离线逆元】
题意:给一棵树,两个参数 k,Lk,Lk,L,需要选择 kkk 个连通块,使得这 kkk 个连通块存在一个公共点,且该公共点到 kkk 个连通块内的任意一点的距离不超过 LLL,求方案数 模 9982 ...
- 【BZOJ5498】[十二省联考2019]皮配(动态规划)
[BZOJ5498][十二省联考2019]皮配(动态规划) 题面 BZOJ 洛谷 题解 先考虑暴力\(dp\),设\(f[i][j][k]\)表示前\(i\)所学校,有\(j\)人在某个阵营,有\(k ...
- [十二省联考2019]春节十二响——长链剖分+堆
题目链接: [十二省联考2019]春节十二响 可以发现每条链上的所有点都要放在不同的段里,那么最多只需要树的深度这么多段就够了. 因为这样可以保证每条链上的点可以放在不同的段中而且一个点放在这些段中一 ...
- 十二省联考 2019 题解
[十二省联考2019]异或粽子 首先异或转前缀和,类似超级钢琴,将三元组 ( l , r , p ) (l,r,p) (l,r,p) 插入堆,表示 s u m [ p ] sum[p] sum[p] ...
- 【十二省联考2019】春节十二响
题面 https://www.luogu.org/problem/P5290 题解 真的是我傻逼,十二省联考$day2$至今还是我的噩梦.$day1$起码一直在调可持久化$trie$树,$day2$真 ...
- HAOI(十二省联考)2019 qwq记
\(\large{Day\ -1}:\) 放假了,白天大概是抱着最后一次在机房的心态复习着板子过去的.看着机房里的各位神仙丝毫不慌的颓倒是有点慌了,敲了一下多项式的板子感觉写的相当自闭,感觉AFO应该 ...
- 十二省联考2019酱油记
在中考前去省选玩一趟. Day -1 对于一个还没有学会所有省选内容的初三Oier来说,这一趟真的是去打酱油的啊.但还是要认真复习. 最近几天在字符串的路上越走越远-晚上才开始复习图论.还有一大堆没有 ...
最新文章
- android 响应点击事件,Android响应事件onClick方法的五种实现方式小结
- urlrewrite实现之HTTP 运行库支持
- matlab练习程序(并行计算)
- 文本处理三剑客之 awk
- 云耀服务器切换系统,【计算】云耀服务器-常见操作汇总指南
- python中类和对象_Python里的类和对象简介
- 如何设置 Notification 中PendingIntent 的 Intent
- MacOS~jenkins里解决docker执行权限问题
- 给员工授予svn相关权限
- C语言程序设计--宏和预处理
- 互联网公司忽悠员工的黑话,套路太深了。。
- 24. yii2 表单赋值 model-load(), model-attributes 方法
- 深度优先搜索解决迷宫最短路径问题
- Extjs的grid总计实现
- 极值点、驻点、拐点的区别和联系
- 量化投资学习-30:股性与人性,从傅里叶变换谈谈股市大V的操作风格的观察
- MT1308芯片原厂
- 基于matlab的-数字调制技术仿真
- 分布式文件存储FastDFS介绍安装部署及相关Java代码编写
- 小学英语词汇量测试软件好,Test Your Vocabulary:号称是最准的英语词汇量测试网站...