• 【题解】[国家集训队] Crash 的文明世界


    [国家集训队] Crash 的文明世界

    ( ext{Solution:})

    要用到的恒等式:

    [n^m=sum_{k=0}^n C_{n}^k k! S(m,k) ]

    考虑化式子:

    [Ans(i)=sum_{j=1}^n dis(i,j)^m ]

    [=sum_{j=1}^n sum_{k=0}^{min{dis(i,j),m}}C_{dis(i,j)}^k k! S(min{dis(i,j),m},k) ]

    [=sum_{k=0}^m k!S(m,k)sum_{j=1}^n C_{dis(i,j)}^k ]

    上面那个式子是直接默认 (mleq dis(i,j)) 了。

    一个细节: 第二类斯特林数的定义是划分成非空集合

    所以才可以像上面的式子一样取 (min.)

    接下来观察一下后面的 (C_{dis(i,j)}^k) 可以理解成从一条路径中选择 (k) 条边的方案数。那这个就可以 (dp) 了。

    (f_{i,j}) 表示子树 (i)(i) 的路径 选择 (j) 条边的方案数。这里之所以是到 (i) 的路径,是因为题目中给的式子是 (dis(i,j)) 从而我们可以把 (i) 当根做。

    于是乎,尝试写出 (dp) 式子:

    [f_{i,j}=sum f_{v,j}+f_{v,j-1} ]

    意义就是:这条路径上选的边是不是一起选上边 ((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) o 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
    ",Ans);
    	}
    	return 0;
    } 
    
  • 相关阅读:
    java虚拟机-内存的分配
    java-类的多态和多重继承
    java 设计模式-策略模式
    java-线程介绍和基本使用
    java 数据流操作
    java basic
    JAVA连载117-反射的应用与打破封装新
    C连载2-编译过程以及语言历史概览
    Android连载12-完善新闻app内容区域
    JavaScript连载11-Switch
  • 原文地址:https://www.cnblogs.com/h-lka/p/15085583.html
Copyright © 2020-2023  润新知