[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 }

 

posted @ 2017-10-23 20:38  skylee03  阅读(115)  评论(0编辑  收藏  举报