【BZOJ 4455】 4455: [Zjoi2016]小星星 (容斥原理+树形DP)

4455: [Zjoi2016]小星星

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 426  Solved: 255

Description

小Y是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有n颗小星星,用m条彩色的细线串了起来,每条细
线连着两颗小星星。有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了n?1条细线,但
通过这些细线,这颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。小Y找到了这个饰品的设
计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,
那么要求对应的小星星原来的图纸上也有细线相连。小Y想知道有多少种可能的对应方式。只有你告诉了她正确的
答案,她才会把小饰品做为礼物送给你呢。

Input

第一行包含个2正整数n,m,表示原来的饰品中小星星的个数和细线的条数。
接下来m行,每行包含2个正整数u,v,表示原来的饰品中小星星u和v通过细线连了起来。
这里的小星星从1开始标号。保证u≠v,且每对小星星之间最多只有一条细线相连。
接下来n-1行,每行包含个2正整数u,v,表示现在的饰品中小星星u和v通过细线连了起来。
保证这些小星星通过细线可以串在一起。
n<=17,m<=n*(n-1)/2

Output

输出共1行,包含一个整数表示可能的对应方式的数量。
如果不存在可行的对应方式则输出0。

Sample Input

4 3
1 2
1 3
1 4
4 1
4 2
4 3

Sample Output

6

HINT

Source

 
【分析】
  很久之前的比赛的一题。【好题啊但是我为什么没有写题解?
  先不考虑每个点一一对应的话,对应关系正确当且仅当树上有的边,对应到原来的无向图上的边也有。
  f[x][i]表示x这个点对应i这个点的情况下,x这棵子树的对应关系正确的方案数。
  这个树形DP即可,n^3。
  但是要保证一一对应,那么可能有一些点没有被对应,所以用容斥。
  枚举没有对应的点,若为奇则减,为偶则加。
  【记得好像不用边目录会T?
  【好像这种“恰好”或者“一一对应”的题目都很经常用容斥,你不能保证要保证的东西,容斥一下就会变得简单
 
  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 using namespace std;
  7 #define INF 0xfffffff
  8 #define Maxn 20
  9 #define LL long long
 10 
 11 bool c[Maxn][Maxn];
 12 int fa[Maxn],first[Maxn];
 13 
 14 int n,m;
 15 
 16 struct node
 17 {
 18     int x,y,next;
 19 }t[2*Maxn];int len;
 20 
 21 void ins(int x,int y)
 22 {
 23     t[++len].x=x;t[len].y=y;
 24     t[len].next=first[x];first[x]=len;
 25 }
 26 
 27 void dfs(int x,int f)
 28 {
 29     fa[x]=f;
 30     for(int i=first[x];i;i=t[i].next) if(t[i].y!=f)
 31     {
 32         dfs(t[i].y,x);
 33     }
 34 }
 35 
 36 LL f[Maxn][Maxn];
 37 
 38 void get_f(int x,int s)
 39 {
 40     for(int i=first[x];i;i=t[i].next) if(t[i].y!=fa[x])
 41     {
 42         get_f(t[i].y,s);
 43     }
 44     for(int i=1;i<=n;i++) if(((1<<i-1)&s)==0)
 45     {
 46         f[x][i]=1;
 47         for(int j=first[x];j;j=t[j].next) if(t[j].y!=fa[x])
 48         {
 49             LL now=0;
 50             int y=t[j].y;
 51             for(int k=1;k<=n;k++) if(c[i][k])
 52             {
 53                 now+=f[y][k];
 54             }
 55             f[x][i]*=now;
 56         }
 57     }
 58     else f[x][i]=0;
 59 }
 60 
 61 int main()
 62 {
 63     memset(c,0,sizeof(c));
 64     scanf("%d%d",&n,&m);
 65     for(int i=1;i<=m;i++)
 66     {
 67         int x,y;
 68         scanf("%d%d",&x,&y);
 69         c[x][y]=c[y][x]=1;
 70     }
 71     memset(first,0,sizeof(first));
 72     for(int i=1;i<n;i++)
 73     {
 74         int x,y;
 75         scanf("%d%d",&x,&y);
 76         ins(x,y);ins(y,x);
 77     }
 78     dfs(1,0);
 79     int mx=(1<<n)-1;
 80     LL ans=0;
 81     for(int i=1;i<=mx;i++)
 82     {
 83         // memset(f,0,sizeof(f));
 84         get_f(1,i);
 85         int h=0,now=i;
 86         LL sum=0;
 87         while(now)
 88         {
 89             if(now&1) h++;
 90             now/=2;
 91         }
 92         for(int j=1;j<=n;j++) sum+=f[1][j];
 93         if(h%2==0) ans+=sum;
 94         else ans-=sum;
 95     }
 96     // memset(f,0,sizeof(f));
 97     get_f(1,0);
 98     for(int j=1;j<=n;j++) ans+=f[1][j];
 99     printf("%lld
",ans);
100     return 0;
101 }
View Code

2017-04-20 17:23:11

原文地址:https://www.cnblogs.com/Konjakmoyu/p/6739856.html