[ZJOI2016]小星星
阿新 • • 發佈:2019-01-11
長度 string 多重 def 但是 brush 去那 很多 sizeof
動態規劃$+$容斥原理:
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("\n"); Dfs(1,0); for(int j=1;j<=n;++j) ret+=f[1][j]; if(size%2) ans-=ret;//printf("%d\n",ans); else ans+=ret; } printf("%lld",ans); return 0; }
[ZJOI2016]小星星