[SimpleOJ236]暴风雨
题目大意:
给你一棵n个点的树,以及m+q条信息。
m条描述点a到b有边直接相连。
q条描述点a和点b的LCA为c。
问有多少符合条件的以1为根的树。
思路:
状压DP。
e[i]记录需要与点i直接相连的点。
sub[i]记录需要在点i子树中的点。
pair[i]记录在点i不同子树下的点对(x,y),即满足lca(x,y)=i。
f[i][j][k]表示以i为根的子树,处理完j个点,子树状压以后的状态为k。
接下来枚举子树j的状态i和子树l的状态k,其中l是j的孩子。
然后每次转移判断一下是否满足m+q个条件。
1 #include<cstdio> 2 #include<cctype> 3 #include<vector> 4 #include<cstring> 5 typedef long long int64; 6 inline int getint() { 7 register char ch; 8 while(!isdigit(ch=getchar())); 9 register int x=ch^'0'; 10 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 11 return x; 12 } 13 const int N=13; 14 int e[N];//e[i]记录需要与点i直接相连的点 15 int sub[N];//sub[i]记录需要在点i子树中的点 16 std::vector<int> pair[N];//pair[i]记录在点i不同子树下的点对(x,y),即满足lca(x,y)=i 17 int64 f[N+1][N+1][1<<N];//f[i][j][k]表示以i为根的子树,处理完j个点,子树状压以后的状态为k 18 inline void init() { 19 memset(e,0,sizeof e); 20 memset(f,0,sizeof f); 21 memset(sub,0,sizeof sub); 22 for(register int i=0;i<N;i++) { 23 f[i][0][1<<i]=1; 24 pair[i].clear(); 25 } 26 } 27 int main() { 28 for(register int T=getint();T;T--) { 29 init(); 30 const int n=getint(),m=getint(),q=getint(); 31 for(register int i=0;i<m;i++) { 32 const int x=getint()-1,y=getint()-1; 33 e[x]|=1<<y; 34 e[y]|=1<<x; 35 } 36 for(register int i=0;i<q;i++) { 37 const int x=getint()-1,y=getint()-1,lca=getint()-1; 38 pair[lca].push_back((1<<x)|(1<<y)); 39 sub[lca]|=(1<<x)|(1<<y); 40 } 41 for(register int i=1;i<(1<<n);i++) {//枚举j子树的状态 42 for(register int j=0;j<n;j++) {//枚举j子树 43 if(!(i&(1<<j))) continue;//j的子树不包括j显然是不可能的 44 if(j&&(i&1)) continue;//1不可能出现在别的子树中 45 for(register int k=i^(1<<j);k;k=(k-1)&(i^(1<<j))) {//枚举l子树的状态 46 for(register unsigned i=0;i<pair[j].size();i++) {//枚举j子树中不能在同一棵子树中的点对 47 if((k&pair[j][i])==pair[j][i]) goto Next;//也就是说肯定不能都出现在l的子树中吧 48 } 49 for(register int l=0;l<n;l++) {//枚举l子树(l是j的一个孩子) 50 if(!(k&(1<<l))) continue;//l的子树不包括l显然是不可能的 51 if((k^(1<<l))&e[j]) continue;//与j相连的点不可能出现在l的子树中(除了l本身) 52 if((e[l]&(k|(1<<j)))!=e[l]) continue;//必须与l相连的点(要么是l孩子,要么是j)没有与l相连 53 if((sub[l]&k)!=sub[l]) continue;//必须出现在l子树中的点都在子树中 54 f[j][l+1][i]+=f[j][l][i-k]*f[l][n][k]; 55 } 56 Next:; 57 } 58 for(register int k=0;k<n;k++) { 59 f[j][k+1][i]+=f[j][k][i]; 60 } 61 } 62 } 63 printf("%lld\n",f[0][n][(1<<n)-1]); 64 } 65 return 0; 66 }