[ZJOI2016]小星星
Posted yexinqwq
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ZJOI2016]小星星相关的知识,希望对你有一定的参考价值。
动态规划$+$容斥原理:
1、暴力状压
我们尝试设计状态:$$f[i][j][S]$$
表示新的图上点$i$对应旧的图上点$j$并且所有点的状态为一个二进制数$S$时的方案数
但是这样做为什么说是暴力呢。。。一看就知道,复杂度爆炸,然而我并不会证$qwq$
2、正解(可能吧):容斥原理
我们思考上面这种方法为什么会爆炸,就是因为有了最后那维状态,如果我们可以省去那个状态的话,就可以降低时间复杂度了,于是我们设:
$$f[i][j]$$表示新的图上点$i$对应旧的图上点$j$时的方案数
那么我们就面临一个问题,就是有很多重复的状态我们都算进来了,那么我们就考虑用容斥原理就好啦
我们发现只要集合长度:$|S|=n$时的方案数减去$|S|=n-1$时的方案数加上$|S|=n-2$时的方案数减去$|S|=n-3$时的方案数$......$
一加一减就阔以了
代码:
#include<iostream> #include<cstdio> #include<cstring> #define ll long long #define N 19 #define M 140 using namespace std; struct Edge { int to,nxt; }edge[N<<1]; int n,m,cnt; ll ans; int ban[N],head[N],g[N][N]; ll f[N][N]; void Add(int u,int v) { edge[++cnt]=(Edge){v,head[u]}; head[u]=cnt; } void Dfs(int u,int fa) { for(int i=1;i<=n;++i) f[u][i]=1; for(int i=head[u];i;i=edge[i].nxt) { int v=edge[i].to; if(v==fa) continue; Dfs(v,u); for(int j=1;j<=n;++j) { ll sum=0; for(int k=1;k<=n;++k) sum+=f[v][k]*(g[k][j]&&ban[j]&&ban[k]); f[u][j]*=sum; } } } signed main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;++i) { int u,v; scanf("%d%d",&u,&v); g[u][v]=g[v][u]=1; } for(int i=1;i<n;++i) { int u,v; scanf("%d%d",&u,&v); Add(u,v); Add(v,u); } for(int i=0;i<(1<<n);++i) { memset(ban,0,sizeof(ban)); ll ret=0; int size=n,now=i; for(int j=1;now;now>>=1,++j) ban[j]=(now&1),size-=(now&1); // for(int j=1;j<=n;++j) // printf("%d ",ban[j]); // printf(" "); Dfs(1,0); for(int j=1;j<=n;++j) ret+=f[1][j]; if(size%2) ans-=ret;//printf("%d ",ans); else ans+=ret; } printf("%lld",ans); return 0; }
以上是关于[ZJOI2016]小星星的主要内容,如果未能解决你的问题,请参考以下文章
[ZJOI2016]小星星&[SHOI2016]黑暗前的幻想乡(容斥)