【日常训练】【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;
}