[ZJOI2016]小星星
Posted Z-Y-Y-S
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ZJOI2016]小星星相关的知识,希望对你有一定的参考价值。
题目描述
小Y是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有n颗小星星,用m条彩色的细线串了起来,每条细线连着两颗小星星。
有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了n?1条细线,但通过这些细线,这颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。小Y找到了这个饰品的设计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,那么要求对应的小星星原来的图纸上也有细线相连。小Y想知道有多少种可能的对应方式。
只有你告诉了她正确的答案,她才会把小饰品做为礼物送给你呢。
输入输出格式
输入格式:
第一行包含个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
输出格式:
输出共1行,包含一个整数表示可能的对应方式的数量。如果不存在可行的对应方式则输出0。
输入输出样例
4 3 1 2 1 3 1 4 4 1 4 2 4 3
6
说明
题解:JudgeOnline/upload/201603/4455.txt
本题题意大致是把树上的n个点赋为互不相同的1~n中的数字,同时满足连接关系,有多少方案
如果要枚举的话,复杂度会O(n!)
这种排列的问题可以转化,令状态k,是一个二进制数,为0则表示该数禁用
对于每一个k,我们求出相应的不被禁用的数的可行排列(不需要满足互不相同)
这时可以用容斥,总方案数=没有禁的方案-禁i的方案-禁j的方案+禁i,j的方案
为什么?因为禁i时,因为不考虑重复,所以可能会有一部分是j未出现的方案,等于禁j的方案
求方案数用树形dp
f[i][j]表示i点编号j的方案
f[i][j]=∏v(∑kf[v][k]) 条件为图中j,k相连
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 struct Node 7 { 8 int next,to; 9 }edge[2001]; 10 int w,q[101],n,m,map[51][51],head[101],num; 11 long long f[21][21],ans; 12 void add(int u,int v) 13 { 14 num++; 15 edge[num].next=head[u]; 16 head[u]=num; 17 edge[num].to=v; 18 } 19 void bit(int x) 20 {int p; 21 p=1;w=0; 22 while (x) 23 { 24 if (x&1) q[++w]=p; 25 p++; 26 x>>=1; 27 } 28 } 29 void dfs(int x,int fa) 30 {int i,j,k; 31 for (i=1;i<=w;i++) 32 f[x][q[i]]=1; 33 for (j=head[x];j;j=edge[j].next) 34 if (edge[j].to!=fa) 35 { 36 dfs(edge[j].to,x); 37 for (i=1;i<=w;i++) 38 {long long s=0; 39 for (k=1;k<=w;k++) 40 if (map[q[i]][q[k]]) 41 { 42 s+=f[edge[j].to][q[k]]; 43 } 44 f[x][q[i]]*=s; 45 } 46 } 47 } 48 int main() 49 {int i,j,u,v; 50 long long sum; 51 cin>>n>>m; 52 for (i=1;i<=m;i++) 53 { 54 scanf("%d%d",&u,&v); 55 map[u][v]=map[v][u]=1; 56 } 57 for (i=1;i<=n-1;i++) 58 { 59 scanf("%d%d",&u,&v); 60 add(u,v);add(v,u); 61 } 62 for (i=1;i<=(1<<n)-1;i++) 63 { 64 bit(i); 65 //cout<<w<<endl; 66 //memset(f,0,sizeof(f)); 67 sum=0; 68 dfs(1,0); 69 for (j=1;j<=w;j++) 70 sum+=f[1][q[j]]; 71 //cout<<sum<<endl; 72 if ((n&1)==(w&1)) ans+=sum; 73 else ans-=sum; 74 } 75 cout<<ans; 76 }
以上是关于[ZJOI2016]小星星的主要内容,如果未能解决你的问题,请参考以下文章
[ZJOI2016]小星星&[SHOI2016]黑暗前的幻想乡(容斥)