zoukankan      html  css  js  c++  java
  • 【题解】CF917D Stranger Trees(prufer序列+二项式反演)

    【题解】CF917D Stranger Trees(prufer序列+二项式反演)

    考虑有一个东西叫做(prufer)序列,然后个东西叫做图联通方案数

    然后我们考虑先算一下至少(k)条边在方案里的方案数,我们可以用树形dp

    (dp(i,j,k))表示对于(i)节点及其子树,共有(j)条边被选中,且和(i)共有(k)个点是在一个联通块里的(prod siz[])之和。转移很简单啦。(但是CF的内存访问极慢!要用对内存友好的写法)

    通过(dp())可以很方便的求出来是(f(k))表示至少有(k)条边在方案里的方案数。

    我们设(g(k))为答案,那么考虑(f(k))(g(k))的关系

    值得注意的事情是,由于我们是在树上选择边,所以我们钦定选择的边是不会成环的,也就是说任何一个选树边的方案都是合法的,所以我们有

    [f(k)=sum_{jge k} {jchoose k} g(j) ]

    根据二项式反演直接得到

    [g(k)=sum_{jge k}{jchoose k}(-1)^{k-j}f(j) ]

    //@winlere
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<assert.h>
    
    using namespace std;  typedef long long ll; 
    inline int qr(){
    	int ret=0,f=0,c=getchar();
    	while(!isdigit(c))f|=c==45,c=getchar();
    	while(isdigit(c)) ret=ret*10+c-48,c=getchar();
    	return f?-ret:ret;
    }
    
    const int maxn=105;
    const int mod=1e9+7;
    int dp[maxn][maxn][maxn],n,siz[maxn],ans[maxn];
    int invs[maxn*maxn],c[maxn][maxn];
    int inv[maxn][maxn*maxn];
    vector<int>e[maxn];
    void add(int fr,int to){e[fr].push_back(to);e[to].push_back(fr);}
    int MOD(const int&x){return x>=mod?x-mod:x;}
    int MOD(const int&x,const int&y){return 1ll*x*y%mod;}
    int MOD(const int&x,const int&y,const int&z,const int&b){return 1ll*x*y%mod*b%mod*z%mod;}
    int ksm(const int&ba,const int&p){
    	int ret=1;
    	for(int t=p,b=ba;t;t>>=1,b=MOD(b,b))
    		if(t&1) ret=MOD(ret,b);
    	return ret;
    }
    
    void pre(const int&n){
    	invs[1]=1;
    	for(int t=2;t<=n;++t) invs[t]=MOD(mod-mod/t,invs[mod%t]),assert(MOD(t,invs[t])==1);
    	for(int t=n+1;t<=n*n;++t) invs[t]=MOD(mod-mod/t,invs[mod%t]);
    	for(int t=1;t<=n*n;++t)
    		for(int i=1;i<=n;++i)
    			inv[i][t]=MOD(i,invs[t]);
    	for(int t=0;t<=n;++t)
    		for(int i=c[t][0]=1;i<=t;++i)
    			c[t][i]=MOD(c[t-1][i-1]+c[t-1][i]);
    	//cerr<<"c[5][2]="<<c[5][2]<<endl;
    }
    
    void dfs(const int&now,const int&last){
    	dp[now][0][1]=1;
    	siz[now]=1;
    	for(auto t:e[now]){
    		if(t^last){
    			dfs(t,now);
    			int g[maxn][maxn];
    			memset(g,0,sizeof g);
    			/*
    			for(int i=siz[now]-1;~i;--i){// edge_totol
    				for(int j=0,edj=min(siz[t]-1,i);j<=edj;++j){// edge_target
    					for(int p=1,edp=siz[t];p<=edp;++p){// vertices_target
    						if(dp[t][j][p]){
    							for(int k=siz[now];k;--k){// vertices_total
    								int&s=g[i][k];
    								//connect
    								if(i-j-1>=0&&k>p) s=(s+1ll*dp[now][i-j-1][k-p]*dp[t][j][p]%mod*inv[k][(k-p)*p])%mod;
    								//dont connect
    								if(i>=j) s=(s+1ll*dp[now][i-j][k]*dp[t][j][p])%mod;
    							}
    						}
    					}
    				}
    			}这种写法很不优秀,内存访问非常不连续!
    			*/
    			for(int i=0;i<siz[now];++i)
    				for(int k=1;k<=siz[now];++k)
    					for(int j=0;j<siz[t];++j)
    						for(int p=1;p<=siz[t];++p){
    							ll l=1ll*dp[now][i][k]*dp[t][j][p]%mod;
    							g[i+j+1][k+p]=(g[i+j+1][k+p]+l*inv[k+p][k*p])%mod;
    							g[i+j][k]=MOD(g[i+j][k]+l);
    						}
    			siz[now]+=siz[t];
    			memcpy(dp[now],g,sizeof g);
    		}
    	}
    }
    
    int rt,val=1e9;
    void dfsrt(int now,int last){
    	siz[now]=1;
    	int k=0;
    	for(auto t:e[now])
    		if(!siz[t])
    			siz[now]+=siz[t],k=max(k,siz[t]);
    	k=max(k,n-siz[now]);
    	if(k<val) rt=now;
    }
    
    int main(){
    	n=qr();
    	pre(101);
    	for(int t=1;t<n;++t) add(qr(),qr());
    	int k=min(n,55);
    	dfs(k,0);
    	for(int e=0;e<=n-1;++e){
    		for(int i=1;i<=e+1;++i)
    			ans[e]=MOD(ans[e]+dp[k][e][i]);
    		if(e<=n-2) ans[e]=MOD(ksm(n,n-2-e),ans[e]);
    		if(e==n-1) ans[e]=1;
    	}
    	for(int t=0;t<=n-1;++t){
    		int ret=0;
    		for(int i=t;i<=n-1;++i){
    			int d=MOD(c[i][t],ans[i]);
    			if((t^i)&1) ret=MOD(ret-d+mod);
    			else ret=MOD(ret+d);
    		}
    		cout<<ret<<' ';
    	}
    	cerr<<endl;
    	return 0;
    }
    
  • 相关阅读:
    C++中左移<<的使用
    学会构造素数序列
    有关lower_bound()函数的使用
    Codeforces Round #166 (Div. 2)
    暴力swap导致TLE问题解决办法
    memset的正确使用
    Codeforces Round #297 (Div. 2)
    Codeforces Round #170 (Div. 2)B
    Codeforces Round #176 (Div. 2)
    C/C++ sort函数的用法
  • 原文地址:https://www.cnblogs.com/winlere/p/12240444.html
Copyright © 2011-2022 走看看