【题解】[国家集训队] Crash 的文明世界
\(\text{Solution:}\)
要用到的恒等式:
考虑化式子:
上面那个式子是直接默认 \(m\leq dis(i,j)\) 了。
一个细节: 第二类斯特林数的定义是划分成非空集合。
所以才可以像上面的式子一样取 \(\min.\)
接下来观察一下后面的 \(C_{dis(i,j)}^k\) 可以理解成从一条路径中选择 \(k\) 条边的方案数。那这个就可以 \(dp\) 了。
设 \(f_{i,j}\) 表示子树 \(i\) 中 到 \(i\) 的路径 选择 \(j\) 条边的方案数。这里之所以是到 \(i\) 的路径,是因为题目中给的式子是 \(dis(i,j)\) 从而我们可以把 \(i\) 当根做。
于是乎,尝试写出 \(dp\) 式子:
意义就是:这条路径上选的边是不是一起选上边 \((v,j).\)
于是这个 \(dp\) 在上述枚举边界的限制下可以做到 \(O(nk)\)
那么现在只求出了一个点对应的 \(Ans(i)\) 怎么办?考虑大力换根:
观察一下,换根的套路一定是从根的孩子换到爹上面。考虑这样造成的影响:
对于要换上去的根 \(x\) 来说,贡献分成两部分:一部分是第一次 \(dp\) 时那棵树中它作为子树的部分;另一部分就是除它之外的部分。而注意到除它之外的部分除了它爹的子树外,还包括和它一起作为其父亲子树的部分。
那就考虑怎么把贡献换上去吧:选择了 \(i\) 条边,对应需要统计:
-
换下去的 \(fa\) 作为其子树,它们之间的连边选不选:\(g_{fa,i-1}+g_{fa,i}\)
-
统计原树中和它一同作为父亲子树的点的答案: \(f_{fa,i}+f_{fa,i-1}\)
-
容斥掉多余的答案:对 \(f_{fa,i}\) 多余的部分是 \(f_{x,i}+f_{x,i-1},\) 对于 \(f_{fa,i-1}\) 多余的部分是 \(f_{x,i-1}+f_{x,i-2}\)
考虑到这三步,做一个换根就好了。还有一个细节:对换根十分不熟悉的菜鸡我写的时候直接就继承了 \(g_{fa}\) 的信息,但实际上是错误的:
主要原因在于,我脑海中想象的换根是已经把 \(fa\) 换上去了,而这时 \(x\) 就对应仅次于根的一棵树。但实际上并不是这样:如果是那样,完全不需要考虑 \(f_{fa}\) 的情况,因为和它同级别的子树其实也就等同于除去它之外的所有点了;但实际上并非如此,换根的时候我们并没有更新所有节点的 \(f,\) 这也意味着这个想法是错误的,实际上我们的树还是原来的模样,我们已经统计出了 \(fa\) 作为根的时候的信息,现在来考虑用原来树上的信息来更新出 \(x\) 作为根的信息。所以,我们需要在原树上进行分析,从而得出上述的结论,不能只凭脑子思考,不画图,这样很容易犯错。
统计出了 \(fa\) 作为根的答案实际上是有很多用处的,比如会发现它其实保留了 \(x\) 在原树中的结构;所以这上面很多信息其实是正确的,其他错误的信息也只是有一部分需要进行转移。
继承了 \(g_{fa}\) 之后思考的就应当是 \(x\) 当根和现在情况的影响:除了 \(x\) 子树内的点,到 \(x\) 的距离都加了 \(1.\)
这一部分考虑在继承的 \(g_{fa}\) 上面完成:可以先把之前的 \(f_{fa}\) 累加进去。因为这一部分在继承的时候是没有改变距离的,因为继承的时候根是父亲。
那么这样一加实际上又加多了,减去原来的部分就是算出 \(f_x\) 对 \(f_{fa}\) 的贡献。
至于其他的部分,继承的时候稍微处理一下就行了。
至于斯特林数直接递推公式大力 \(O(k^2)\) 处理即可。
总复杂度 \(O(nk+k^2)\to O(nk).\)
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const int mod=10007;
inline int Add(int x,int y){return (x+y)%mod;}
inline int Mul(int x,int y){return 1ll*x*y%mod;}
inline int dec(int x,int y){return (x-y+mod)%mod;}
int S[200][200],n,k,f[N][200],g[N][200];
int head[N],tot,Ans[N],fac[N];
inline int Min(int x,int y){return x<y?x:y;}
inline int Max(int x,int y){return x>y?x:y;}
struct E{int nxt,to;}e[N];
inline void add(int x,int y){
e[++tot]=(E){head[x],y};
head[x]=tot;
}
inline int qpow(int a,int b){
int res=1;
while(b){
if(b&1)res=Mul(res,a);
a=Mul(a,a);b>>=1;
}
return res;
}
void Spre(){
S[0][0]=S[1][1]=1;
for(int i=2;i<=k;++i)
for(int j=1;j<=i;++j)
S[i][j]=Add(S[i-1][j-1],Mul(j,S[i-1][j]));
fac[0]=1;
for(int i=1;i<=n;++i)fac[i]=Mul(fac[i-1],i);
}
void dfs(int x,int fa){
f[x][0]=1;
for(int i=head[x];i;i=e[i].nxt){
int j=e[i].to;
if(j==fa)continue;
dfs(j,x);
for(int v=1;v<=k;++v)f[x][v]=Add(f[x][v],Add(f[j][v],f[j][v-1]));
f[x][0]=Add(f[x][0],f[j][0]);
}
}
void change(int x,int fa){
if(fa){
for(int i=0;i<=k;++i){
if(i==0)g[x][i]=Add(g[fa][0],Add(f[fa][0],mod-f[x][0]));
else if(i==1)g[x][i]=Add(g[fa][i],Add(g[fa][i-1],Add(f[fa][i],Add(f[fa][i-1],Add(mod-f[x][i],mod-f[x][i-1]+mod-f[x][i-1])))));
else {
g[x][i]=Add(g[fa][i],Add(g[fa][i-1],Add(f[fa][i],Add(f[fa][i-1],Add(mod-f[x][i],mod-f[x][i-1]+mod-f[x][i-1])))));
g[x][i]=Add(g[x][i],mod-f[x][i-2]);
}
}
}
for(int i=head[x];i;i=e[i].nxt){
int j=e[i].to;
if(j==fa)continue;
change(j,x);
}
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
Spre();
dfs(1,0);change(1,0);
for(int i=1;i<=n;++i){
int Ans=0;
for(int j=0;j<=k;++j){
int v=Mul(fac[j],S[k][j]);
Ans=Add(Ans,Mul(v,f[i][j]+g[i][j]));
}
printf("%d\n",Ans);
}
return 0;
}