移动端没做适配,所以看起来十分诡异是正常的

【日常训练】【CF】20200605_旅行_图论/树形dp

题面

题解

现场得分:40/100

一个接在环上的树上面统计答案的时候,我没有讨论清楚,出了点小事。

下发题解

另外的补充

  • 关于把环扔掉:我认为这个东西还是用拓扑来搞比较好,代码又短又好写。只用每回把度数为一的弄出来就好了。
  • 关于树上统计:如果是全都取满的,那么就是比较经典的树形dp问题。如果是有东西还能取,但是不取了,我们可以枚举根,强制令根不取,计算有多少种,最后每种取的个数的方案数除以这种方案下未取的点的个数就行了。
  • 关于树上统计的优化:我们可以用换根dp,就能做\(O(n^2)\)了。wyj的题解

代码

#include<bits/stdc++.h>
#define LL long long
#define MAXN 210
#define MOD 1000000009
using namespace std;
template<typename T>void Read(T &cn)
{
	char c; int sig = 1;
	while(!isdigit(c = getchar())) if(c == '-') sig = -1; cn = c-48;
	while(isdigit(c = getchar())) cn = cn*10+c-48; cn*=sig;
}
template<typename T>void Write(T cn)
{
	if(cn < 0) {putchar('-'); cn = 0-cn; }
	int wei = 0; T cm = 0; int cx = cn%10; cn/=10;
	while(cn) wei++, cm = cm*10+cn%10, cn/=10;
	while(wei--) putchar(cm%10+48), cm /= 10;
	putchar(cx+48);
}
template<typename T>void Max(T &cn, T cm) {cn = cn < cm ? cm : cn; }
template<typename T>void Min(T &cn, T cm) {cn = cn < cm ? cn : cm; }
int n, m;
int t[MAXN+1][MAXN+1];
namespace Sub1{
	int pan() {return n <= 20; }
	int f[1<<20];
	int you[MAXN+1];
	int ans[MAXN+1];
	int suan(int cn)
	{
		if(f[cn] != -1) return f[cn];
		f[cn] = 0; 
		for(int i = 1;i<=n;i++) you[i] = (cn & (1<<(i-1)))!=0;
		for(int i = 1;i<=n;i++) if(you[i])
		{
			int ge = 0;
			for(int j = 1;j<=n;j++) if(!you[j]) ge += t[i][j];
			if(ge >= 2) return f[cn];
		} 
		for(int i = 1;i<=n;i++) if(you[i]) f[cn] = (f[cn] + suan(cn^(1<<(i-1))))%MOD;
		int ge = 0;
		for(int i = 1;i<=n;i++) ge += you[i];
		ans[ge] = (ans[ge] + f[cn])%MOD;
		return f[cn];
	}
	int main()
	{
		for(int i = 0;i<(1<<n);i++) f[i] = -1;
		memset(ans,0,sizeof(ans));
		f[0] = 1; ans[0]++;
		for(int i = 1;i<(1<<n);i++) if(f[i] == -1) suan(i);
		for(int i = 0;i<=n;i++) Write(ans[i]), puts(""); 
		return 0;
	}
};
namespace Sub2{
	int pan() {return 1; }
	struct qwe{
		int a,b,ne;
		void mk(int cn, int cm, int cx) {a = cn; b = cm; ne = cx; }
	};
	qwe a[MAXN*MAXN+1];
	int alen;
	int head[MAXN+1];
	int du[MAXN+1], vis[MAXN+1], you[MAXN+1];
	int dui[MAXN+1], l, r;
	int jie[MAXN+1], nij[MAXN+1];
	int zhan[MAXN+1], zlen;
	int da[MAXN+1], dao[MAXN+1];
	int f[MAXN+1][MAXN+1];
	int ans1[MAXN+1], ans2[MAXN+1], ans3[MAXN+1];
	int Eshu;
	LL ksm(LL cn, LL cm) {LL ans = 1; while(cm) ans = ans*(1+(cn-1)*(cm&1))%MOD, cn = cn*cn%MOD, cm>>=1; return ans; }
	LL zuhe(int cn, int cm) {return 1ll*jie[cn]*nij[cm]%MOD*nij[cn-cm]%MOD; }
	void lian(int cn, int cm) {a[++alen].mk(cn, cm, head[cn]); head[cn] = alen; }
	void yuchu(int cn)
	{
		jie[0] = 1;
		for(int i = 1;i<=cn;i++) jie[i] = 1ll*jie[i-1]*i%MOD;
		nij[cn] = ksm(jie[cn], MOD-2);
		for(int i = cn-1;i>=0;i--) nij[i] = 1ll*nij[i+1]*(i+1)%MOD;
	}
	void dfs_z(int cn, int fa)
	{
		zhan[++zlen] = cn;
		you[cn] = 1;
		for(int i = head[cn];i;i = a[i].ne) 
		{
			int y = a[i].b;
			if(!vis[y]) dao[cn] = 0;
			if(y == fa || !vis[y]) continue;
			dfs_z(y, cn); 
		}
	}
	void dfs1(int cn, int fa)
	{
		dao[cn] = 1;
		for(int i = head[cn];i;i = a[i].ne) 
		{
			int y = a[i].b;
			if(!vis[y]) dao[cn] = -1;
			if(y == fa || !vis[y]) continue;
			dfs1(y, cn); if(dao[y] != 1 && dao[cn] != -1) dao[cn] = 0;
		}
		da[cn] = (dao[cn] == 1);
		for(int i = head[cn];i;i = a[i].ne)
		{
			int y = a[i].b;
			if(y == fa || !vis[y]) continue;
			da[cn] += da[y];
		}
	}
	void dfs2(int cn, int fa)
	{
		if(!fa) for(int i = 0;i<=zlen;i++) f[cn][i] = 0;
		else for(int i = 0;i<=da[cn];i++) f[cn][i] = 0;
		f[cn][0] = 1; int lei = 0;
		for(int i = head[cn];i;i = a[i].ne)
		{
			int y = a[i].b;
			if(y == fa || !vis[y]) continue;
			dfs2(y, cn);
			for(int j = 0;j<=lei+da[y];j++) ans3[j] = 0;
			for(int j = 0;j<=lei;j++) for(int k = 0;k<=da[y];k++) ans3[j+k] = (ans3[j+k] + 1ll*f[cn][j]*f[y][k]%MOD*zuhe(j+k,j))%MOD;
			lei += da[y];
			for(int j = 0;j<=lei;j++) f[cn][j] = ans3[j];
		}
		if(dao[cn] == 1) f[cn][da[cn]] = f[cn][da[cn]-1];
		if(dao[cn] == -1 && !fa) f[cn][zlen] = f[cn][zlen-1];
	}
	int main()
	{
		alen = 0; memset(head,0,sizeof(head)); memset(du,0,sizeof(du));
		for(int i = 1;i<=n;i++) for(int j = 1;j<=n;j++) if(t[i][j]) lian(i,j), du[i]++;
	
		l = r = 0; Eshu = 0;
		memset(vis,0,sizeof(vis));
		for(int i = 1;i<=n;i++) if(du[i] <= 1) dui[++r] = i, vis[i] = 1;
		while(l < r)
		{
			int dang = dui[++l];
			for(int i = head[dang];i;i = a[i].ne)
			{
				int y = a[i].b;
				if(vis[y]) continue;
				du[y]--;
				if(du[y] == 1) dui[++r] = y, vis[y] = 1;
			}
		}
		
		if(r == 0) {
			puts("1");
			for(int i = 1;i<=n;i++) puts("0");
			return 0;
		}
		yuchu(r); memset(you,0,sizeof(you));
		memset(ans1, 0, sizeof(ans1)); ans1[0] = 1;
		for(int i = 1;i<=n;i++) if(vis[i] && !you[i]) 
		{
			zlen = 0; dfs_z(i, 0);
			memset(ans2, 0, sizeof(ans2));
			for(int j = 1;j<=zlen;j++)
			{
				dfs1(zhan[j], 0); dfs2(zhan[j], 0);
				for(int k = 0;k<=zlen;k++) ans2[k] = (ans2[k] + f[zhan[j]][k])%MOD;
			}
			for(int j = 0;j<zlen;j++) ans2[j] = 1ll*ans2[j]*ksm(zlen-j, MOD-2)%MOD;
			for(int j = 0;j<=n;j++) ans3[j] = 0;
			for(int j = 0;j<=n;j++) for(int k = 0;k<=zlen;k++) ans3[j+k] = (ans3[j+k] + 1ll*ans1[j]*ans2[k]%MOD*zuhe(j+k,j))%MOD;
			for(int j = 0;j<=n;j++) ans1[j] = ans3[j];
		}
		for(int i = 0;i<=n;i++) Write(ans1[i]), puts("");
		return 0;
	}
};
int main()
{
//	freopen("travel.in","r",stdin);
//	freopen("travel.out","w",stdout);
	Read(n); Read(m);
	for(int i = 1;i<=n;i++) for(int j = 1;j<=n;j++) t[i][j] = 0;
	for(int i = 1;i<=m;i++) {int bx,by; Read(bx); Read(by); t[bx][by] = t[by][bx] = 1; }
//	if(Sub1::pan()) return Sub1::main();
	if(Sub2::pan()) return Sub2::main();
	return 0;
}
posted @ 2020-06-05 18:27  czyarl  阅读(64)  评论(0编辑  收藏  举报