• Codeforces 512D


    题面传送门

    题意:
    给出一张无向图,每次你可以选择一个度数 (leq 1) 的点并将其删除。
    问对于 (k=0,1,2,dots,n),有多少个删除 (k) 个点的序列,答案模 (10^9+9)
    (1leq nleq 100)

    很容易发现,环上的点是永远不可能被删除的。于是我们对原图做一遍拓扑排序,求出哪些点可能被删除。
    显然这些能被删除的点一定会形成一棵森林。
    而森林中的每个连通块又可以分为两类:一种是该连通块在原图中属于的连通块中有环相连。另一种是该连通块在原图中本身就是一棵树。
    对于第一种情况,一定有且仅有一个点 (x) 与环相邻。此时 (x) 一定是连通块中最后一个被删除的。我们就以 (x) 为根跑一遍树上背包。具体来说,设 (dp_{i,j}) 表示 (i) 子树中选了 (j) 个点的方案数。由于我们这是有根树,必须选完 (x) 子树内所有点才能选 (x)。转移就正常求个卷积,再乘上个组合数的系数即可。由于任意两点之间最多被合并一次,复杂度 (n^2)
    对于第二种情况,我们并没有给这棵树定根。所以我们考虑以树上每个点为根都跑一次树上背包。但这样会重复计算。对于选了 (i) 个点的方案,在以未被选择 (s-i) 个点为根情况下都被算了一次。故答案除以 (s-i)
    然后最后再总体跑一遍背包即可。时间复杂度 (mathcal O(n^3))

    #include <bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
    #define fz(i,a,b) for(int i=a;i<=b;i++)
    #define fd(i,a,b) for(int i=a;i>=b;i--)
    #define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
    #define fill0(a) memset(a,0,sizeof(a))
    #define fill1(a) memset(a,-1,sizeof(a))
    #define fillbig(a) memset(a,63,sizeof(a))
    #define pb push_back
    #define ppb pop_back
    #define mp make_pair
    template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
    template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
    typedef pair<int,int> pii;
    typedef long long ll;
    template<typename T> void read(T &x){
    	x=0;char c=getchar();T neg=1;
    	while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
    	while(isdigit(c)) x=x*10+c-'0',c=getchar();
    	x*=neg;
    }
    const int MAXN=100;
    const int MAXM=4950;
    const int MOD=1e9+9;
    int qpow(int x,int e){int ret=1;for(;e;x=1ll*x*x%MOD,e>>=1) if(e&1) ret=1ll*ret*x%MOD;return ret;}
    int n,m,deg[MAXN+5],to[MAXM*2+5],ec=0,nxt[MAXM*2+5],hd[MAXN+5];
    void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
    bool vis[MAXN+5];
    int from[MAXN+5],cmp=0,siz[MAXN+5],rt[MAXN+5],con[MAXN+5],c[MAXN+5][MAXN+5];
    void dfs0(int x){
    	if(from[x]) return;from[x]=cmp;siz[cmp]++;
    //	printf("%d %d
    ",x,cmp);
    	for(int e=hd[x];e;e=nxt[e]) if(vis[to[e]]) dfs0(to[e]);
    }
    int dp[MAXN+5][MAXN+5],s[MAXN+5],tmp[MAXN+5],dpc[MAXN+5][MAXN+5];
    void dfs(int x,int f){
    //	printf("%d %d
    ",x,f);
    	s[x]=1;dp[x][0]=1;
    	for(int e=hd[x];e;e=nxt[e]){
    		int y=to[e];if(y==f) continue;dfs(y,x);
    		memset(tmp,0,sizeof(tmp));
    		for(int i=0;i<=s[x];i++) for(int j=0;j<=s[y];j++)
    			tmp[i+j]=(tmp[i+j]+1ll*dp[x][i]*dp[y][j]%MOD*c[i+j][i]%MOD)%MOD;
    		s[x]+=s[y];
    		for(int i=0;i<=s[x];i++) dp[x][i]=tmp[i];
    	} dp[x][s[x]]=dp[x][s[x]-1];
    //	for(int i=0;i<=s[x];i++) printf("%d %d %d
    ",x,i,dp[x][i]);
    }
    int iv[MAXN+5],ans[MAXN+5]; 
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=0;i<=MAXN;i++){
    		c[i][0]=1;
    		for(int j=1;j<=i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD;
    	}
    	iv[0]=1;for(int i=1;i<=MAXN;i++) iv[i]=qpow(i,MOD-2);
    	for(int i=1;i<=m;i++){
    		int u,v;scanf("%d%d",&u,&v);
    		adde(u,v);adde(v,u);deg[u]++;deg[v]++;
    	}
    	queue<int> q;
    	for(int i=1;i<=n;i++) if(deg[i]<=1) q.push(i);
    	while(!q.empty()){
    		int x=q.front();q.pop();vis[x]=1;//printf("%d
    ",x);
    		for(int e=hd[x];e;e=nxt[e]){
    			int y=to[e];deg[y]--;
    			if(deg[y]<=1&&!vis[y]) q.push(y);
    		}
    	}
    	for(int i=1;i<=n;i++) if(vis[i]&&!from[i]) cmp++,dfs0(i);
    	for(int i=1;i<=n;i++) for(int e=hd[i];e;e=nxt[e]) if(vis[i]&&!vis[to[e]]) con[i]=to[e],rt[from[i]]=i;
    //	for(int i=1;i<=cmp;i++) printf("%d
    ",rt[i]);
    	for(int i=1;i<=n;i++) if(vis[i]){
    		if(rt[from[i]]&&i==rt[from[i]]){
    			memset(dp,0,sizeof(dp));memset(s,0,sizeof(s));
    			dfs(i,con[i]);
    			for(int j=0;j<=siz[from[i]];j++) dpc[from[i]][j]=dp[i][j];
    		} else if(!rt[from[i]]){
    			memset(dp,0,sizeof(dp));memset(s,0,sizeof(s));
    			dfs(i,0);//printf("%d
    ",i);
    //			for(int j=1;j<=siz[from[i]];j++) printf("%d ",dp[i][j]);printf("
    ");
    			for(int j=0;j<=siz[from[i]];j++) dpc[from[i]][j]=(dpc[from[i]][j]+dp[i][j])%MOD;
    		}
    	}
    	for(int i=1;i<=cmp;i++) if(!rt[i]){
    		for(int j=0;j<=siz[i];j++) dpc[i][j]=1ll*dpc[i][j]*iv[siz[i]-j]%MOD;
    	} ans[0]=1;
    //	for(int i=1;i<=cmp;i++) for(int j=0;j<=siz[i];j++) printf("%d %d %d
    ",i,j,dpc[i][j]); 
    	for(int i=1;i<=cmp;i++) for(int j=n;j;j--) for(int k=1;k<=min(siz[i],j);k++)
    		ans[j]=(ans[j]+1ll*ans[j-k]*dpc[i][k]%MOD*c[j][k])%MOD;
    	for(int i=0;i<=n;i++) printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    液晶显示器分辨设置,显示器分辨率设置……
    如何显示语言栏
    查看一键Ghost的备份文件
    百度空间的变迁
    CentOS U盘安装
    Linux服务器系统选择
    博客一夜回到解放前
    spark常见的transformation和action算子
    二分查找
    9:两个栈实现一个队列
  • 原文地址:https://www.cnblogs.com/ET2006/p/Codeforces-512D.html
Copyright © 2020-2023  润新知