洛谷連接:https://www.luogu.org/problemnew/show/P3349
題意至關於給一棵樹從新賦予彼此不一樣的編號,要求樹上相鄰的兩個節點在給定的另一個無向圖中也存在邊相連。
n很小,但枚舉階乘確定是會爆炸的。
發現編號彼此不一樣對統計答案的影響太大了,咱們能夠嘗試先讓編號能夠重複,可是限制能夠選用的編號集,即O(2^n)枚舉n個數的子集,而後容斥一下答案。
可選用的編號集合肯定了,編號還能夠重複,接下來直接跑樹形dp就能夠了。f(u)(j)存的是u節點映射向j,子樹內的總方案數。c++
#include<bits/stdc++.h> using namespace std; const int N=40; typedef long long ll; #define rep(i,a,b) for(register int i=(a);i<=(b);++i) #define il inline int gr,h[N],nxt[N],to[N]; il void tu(int x,int y){to[++gr]=y,nxt[gr]=h[x],h[x]=gr;} int n,m,mp[N][N],p[N],tot; ll ans,dp[18][18],tmp; void dfs(int u,int f){ rep(j,1,tot)dp[u][j]=1; for(int i=h[u];i;i=nxt[i]){ int d=to[i]; if(d==f)continue; dfs(d,u); rep(j,1,tot){ tmp=0; rep(k,1,tot){ if(mp[p[j]][p[k]]) tmp+=dp[d][k]; } dp[u][j]*=tmp; } } } int main(){ scanf("%d%d",&n,&m); int a,b; rep(i,1,m)scanf("%d%d",&a,&b),mp[a][b]=mp[b][a]=1; rep(i,1,n-1)scanf("%d%d",&a,&b),tu(a,b),tu(b,a); rep(j,1,(1<<n)-1){tot=0; rep(i,0,n-1){ if((j>>i)&1)p[++tot]=i+1; } dfs(1,0);tmp=0; rep(i,1,tot)tmp+=dp[1][i]; ans+=(((n-tot)&1)?-1ll:1ll)*tmp; } printf("%lld\n",ans); return 0; }