zoukankan      html  css  js  c++  java
  • P4827「国家集训队」 Crash 的文明世界

    「国家集训队」 Crash 的文明世界

    提供一种不需要脑子的方法。

    其实是看洛谷讨论版看出来的(

    (但是全网也就这一篇这个方法的题解了)

    首先这是一个关于树上路径的问题,我们可以无脑上点分治。

    考虑当以 (root) 为根时,如何计算经过 (root) 的路径对某一个点的贡献。

    若现在我们要找经过 (root) 的路径中长度为 (d) 且路径的一端为 (u)

    则这一部分的贡献为 (v_{d}cnt_{d-h_u}),其中 (v_d=d^k)(h_u) 表示点 (u) 的深度,(cnt_i) 表示深度为 (i) 的节点个数。

    当然这里会有一种不合法的情况,就是找到的路径两端点在 (root) 的同一棵子树中。这可以用点分治惯用的容斥解决。

    (root) 为根时,路径对点 (u) 的贡献为(事实上对深度为 (h_u) 的节点贡献是相同的)

    [sum_{d=h_u}^{maxdeep+h_u}v_dcnt_{d-h_u}\ ]

    为了处理起来更加方便,我们增加一些无用的部分

    于是有

    [sum_{d=0}^{2 imes maxdeep}v_dcnt_{d-h_u}\ ]

    (n=2 imes maxdeep)

    [sum_{d=0}^{n}v_dcnt_{d-h_u}\ ]

    按照套路,将 (cnt) 数组翻转一下

    [sum_{d=0}^{n}v_dcnt_{n-d+h_u}\ ]

    [Ans_{n+h_u}=sum_{d=0}^{n}v_dcnt_{n-d+h_u}\ ]

    这是一个卷积的形式,直接 ( exttt{FFT/NTT}) 即可。

    所以总时间复杂度为 (O(nlog_2^2n))

    (所以为啥不把这题的 k 开到和 n 同级呢)

    下面讲讲常数优化:

    • 预处理原根、单位根必不可少。
    • 能不取模尽量别取模。
    • 由于这也是在分治的过程中进行 ( exttt{FFT}) 的计算,所以当规模较小时暴力会更快。

    另外值得注意的是,由于本题的模数不是一个 ( exttt{NTT}) 模数,而中间过程中的结果最大可能为 (10006^2) ,所以我们可能得选择恰当的 ( exttt{NTT}) 模数。

    这样的话结果就一定不会有问题。

    这个题就这样非常套路地被我们解决了。

    贴一个很丑的代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+5;
    const int p=1e4+7;
    const int P=1004535809;
    int n,k;
    struct edge{
    	int to,nex;
    }e[maxn<<1];
    int head[maxn],tot;
    int siz[maxn],dp[maxn],vis[maxn],rt;
    int w[maxn],cnt[maxn],ans[maxn];
    int f[maxn],g[maxn],rev[maxn],len=1;
    void add(int a,int b){
    	e[++tot]=(edge){b,head[a]};
    	head[a]=tot;
    }
    int ksm(int a,int b,int p){
    	int ans=1;
    	while(b){
    		if(b&1) ans=1ll*ans*a%p;
    		b>>=1,a=1ll*a*a%p;
    	}
    	return ans;
    }
    vector<int> W[20];
    void INIT(){
    	for(int i=1,num=0;num<=17;++num,i<<=1){
    		int w=ksm(3,(P-1)/(i<<1),P),tmp=1;
    		for(int k=0;k<i;++k)
    			W[num].emplace_back(tmp),tmp=1ll*tmp*w%P;
    	}
    }
    void NTT(int *f){
    	for(int i=0;i<len;++i)
    		if(i<rev[i]) swap(f[i],f[rev[i]]);
    	for(int i=1,num=0;i<len;i<<=1,++num){
    		for(int j=0;j<len;j+=(i<<1)){
    			for(int k=0;k<i;++k){
    				int x=f[j|k],y=1ll*W[num][k]*f[i|j|k]%P;
    				f[j|k]=x+y>P?x+y-P:x+y;
    				f[i|j|k]=x-y<0?x-y+P:x-y;
    			}
    		}
    	}
    }
    void init(int x){
    	len=1;
    	while(len<=x) len<<=1;
    	f[0]=g[0]=0;
    	for(int i=1;i<len;++i)
    		rev[i]=rev[i>>1]>>1|((i&1)?len>>1:0);
    	memset(f,0,sizeof (int)*len);
    	memset(g,0,sizeof (int)*len);
    }
    void getroot(int u,int f,int sum){
    	siz[u]=1,dp[u]=0;
    	for(int i=head[u];i;i=e[i].nex){
    		int v=e[i].to;
    		if(v==f||vis[v]) continue;
    		getroot(v,u,sum);
    		siz[u]+=siz[v];
    		dp[u]=max(siz[v],dp[u]);
    	}
    	dp[u]=max(dp[u],sum-siz[u]);
    	if(dp[u]<dp[rt]) rt=u;
    }
    void clear(int u,int f,int dis,int &mx){
    	mx=max(mx,dis);
    	for(int i=head[u];i;i=e[i].nex){
    		int v=e[i].to;
    		if(v==f||vis[v]) continue;
    		clear(v,u,dis+1,mx);
    	}
    }
    void getdis(int u,int f,int dis){
    	++cnt[dis];
    	if(cnt[dis]>=p) cnt[dis]-=p;
    	for(int i=head[u];i;i=e[i].nex){
    		int v=e[i].to;
    		if(v==f||vis[v]) continue;
    		getdis(v,u,dis+1);
    	}
    }
    int owo[251];
    void mul(int *a,int *b,int n){
    	if(n<=100){
    		memset(owo,0,sizeof (int)*(2*n+1));
    		for(int i=0;i<=n;++i)
    			for(int j=0;j<=n;++j)
    				owo[i+j]=owo[i+j]+1ll*a[i]*b[j]%P>P?owo[i+j]+1ll*a[i]*b[j]%P-P:owo[i+j]+1ll*a[i]*b[j]%P;
    		for(int i=0;i<=2*n;++i) a[i]=owo[i];
    		return ;
    	}
    	memcpy(f,a,sizeof (int)*(n+1));
    	memcpy(g,b,sizeof (int)*(n+1));
    	NTT(f),NTT(g);
    	for(int i=0;i<len;++i) f[i]=1ll*f[i]*g[i]%P;
    	NTT(f);
    	reverse(f+1,f+len);
    	int inv=ksm(len,P-2,P);
    	for(int i=0;i<=2*n;++i) a[i]=1ll*f[i]*inv%P;
    }
    void dfs(int u,int f,int dis,int opt){
    	ans[u]+=opt*cnt[dis];
    	for(int i=head[u];i;i=e[i].nex){
    		int v=e[i].to;
    		if(v==f||vis[v]) continue;
    		dfs(v,u,dis+1,opt);
    	}
    }
    void calc(int u,int dis,int opt){
    	int n=0;
    	clear(u,0,dis,n);n*=2;
    	memset(cnt,0,sizeof (int)*(n+1));
    	getdis(u,0,dis);
    	reverse(cnt,cnt+n+1);
    	init(2*n);
    	mul(cnt,w,n);
    	for(int i=0;i<=n;++i) cnt[i]=cnt[i+n];
    	dfs(u,0,dis,opt);
    }
    void solve(int u){
    	vis[u]=1;
    	calc(u,0,1);
    	for(int i=head[u];i;i=e[i].nex){
    		int v=e[i].to;
    		if(vis[v]) continue;
    		calc(v,1,-1);
    		rt=0;
    		getroot(v,0,siz[v]);
    		solve(rt);
    	}
    }
    int main(){
    //	freopen("1.in","r",stdin);
    //	freopen("1.out","w",stdout);
    	ios::sync_with_stdio(0);
    	cin.tie(0),cout.tie(0);
    	cin>>n>>k;
    	INIT();
    	for(int i=1;i<n;++i){
    		int a,b;cin>>a>>b;
    		add(a,b),add(b,a);
    	}
    	for(int i=0;i<n;++i) w[i]=ksm(i,k,p);
    	dp[0]=(1<<30);
    	getroot(1,0,n);
    	solve(rt);
    	for(int i=1;i<=n;++i) cout<<ans[i]%p<<'
    ';
    }
    
  • 相关阅读:
    在Activity/Fragment以外使用Toast【转】
    Android笔记(四十七) Android中的数据存储——XML(三)SAX解析
    Android笔记(四十六) Android中的数据存储——XML(二)PULL解析
    Python的包(Packages)
    编写你自己的Python模块
    了解Python控制流语句——continue 语句
    了解Python控制流语句——break 语句
    了解Python控制流语句——for 循环
    了解Python控制流语句——while 语句
    了解Python控制流语句——if语句
  • 原文地址:https://www.cnblogs.com/HenryHuang-Never-Settle/p/solution-P4827.html
Copyright © 2011-2022 走看看