【BZOJ 4455】 4455: [Zjoi2016]小星星 (容斥原理+树形DP)
4455: [Zjoi2016]小星星
Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 426 Solved: 255Description
小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)/2Output
输出共1行,包含一个整数表示可能的对应方式的数量。如果不存在可行的对应方式则输出0。Sample Input
4 3
1 2
1 3
1 4
4 1
4 2
4 3Sample Output
6HINT
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\n",ans); 100 return 0; 101 }
2017-04-20 17:23:11