容斥原理+樹形DP.ios
這道題用的容斥思想很是妙啊!主要的思路就是讓全部點與S集合中的點對應,能夠重複對應,而且能夠不用對應徹底(意思是是S的子集也能夠).這樣他有未對應徹底的,那就減去,從全都一一對應到少對應幾個,減號套減號,就造成了容斥關係,看S中元素個數與n的關係,若是相差奇數個,那就減去,相差偶數個,那就加上.用樹形DP轉移,枚舉當前節點選哪個,再枚舉子節點選哪個,若是兩個有連線就統計到答案裏.由於每一個節點只進入一次,轉移是 \(n^2\) 的,枚舉子集是 \(2^n\) 總複雜度就是 \(O(2^nn^3)\)ide
/************************************************************** Problem: 4455 User: BeiYu Language: C++ Result: Accepted Time:6200 ms Memory:1296 kb ****************************************************************/ #include<cstdio> #include<vector> #include<iostream> using namespace std; const int N = 18; typedef long long LL; int n,m,S;LL ans; LL f[N][N]; int q[N],t,pow2[N]; bool b[N][N]; vector<int> g[N]; void DP(int x,int fa=0){ for(int i=1;i<=t;i++) f[x][q[i]]=1; for(int i=0,v;i<g[x].size();i++) if((v=g[x][i])!=fa){ DP(v,x); for(int j=1;j<=t;j++){ LL tmp=0; for(int k=1;k<=t;k++) if(b[q[j]][q[k]]) tmp+=f[v][q[k]]; f[x][q[j]]*=tmp; } } } void calc(const int &S){ t=0;for(int i=1;i<=n;i++) if(S&pow2[i-1]) q[++t]=i; } inline int in(int x=0,char ch=getchar()){ while(ch>'9'||ch<'0') ch=getchar(); while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x; } int main(){ n=in(),m=in();pow2[0]=1;for(int i=1;i<18;i++) pow2[i]=pow2[i-1]<<1; for(int i=1,u,v;i<=m;i++) u=in(),v=in(),b[u][v]=b[v][u]=1; for(int i=1,u,v;i<n;i++) u=in(),v=in(),g[u].push_back(v),g[v].push_back(u); for(S=1;S<(1<<n);S++){ calc(S);DP(1,0);LL tmp=0; for(int i=1;i<=t;i++) tmp+=f[1][q[i]]; if((n-t)&1) ans-=tmp;else ans+=tmp; }return printf("%lld\n",ans),0; }