dtoj2612 小星星(star)
小Y是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有n颗小星星,用m条彩色的细线串了起来,每条细线连着两颗小星星。有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了n-1条细线,但通过这些细线,这n颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。
小Y找到了这个饰品的设计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,那么要求对应的小星星原来的图纸上也有细线相连。
小Y想知道有多少种可能的对应方式。只有你告诉了她正确的答案,她才会把小饰品做为礼物送给你呢。
Sol
题目求树在图上有多少种对应方式。
假设我们先不考虑每一个点只能映射一个点。(这步很关键!!使得状态好设计、转移)
我们在最外层枚举点集S,表示树映射到了点集S或者S的子集的方案数。
此时的映射树上两个不同的点可以对应图上的同一个点。
我们考虑f[i][j]表示以i为根的子树,i对应的点是j。枚举i的儿子v对应点k,(j,k)要有边。
考虑n个点映射但不合法的方案,一定有1个点未被映射。
那么我们减去n-1个点被映射的方案。
那么此时只映射n-2个点的方案被减了2次,要加上
答案就是映射到n个点的方案数-映射n-1个点的方案数+映射n-2个点的方案数。
我是想不出这种题的,好菜,还是得多做题。
#include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define maxn 20 using namespace std; int n,m,s[maxn][maxn],head[maxn]; int t1,t2,tot,dy[maxn],top; long long f[maxn][maxn],ans; struct node{ int v,nex; }e[805]; void lj(int t1,int t2){ e[++tot].v=t2;e[tot].nex=head[t1];head[t1]=tot; } void dfs(int k,int fa){ for(int i=head[k];i;i=e[i].nex){ if(e[i].v!=fa)dfs(e[i].v,k); } for(int i=1;i<=top;i++){ f[k][i]=1; for(int j=head[k];j;j=e[j].nex){ if(e[j].v==fa)continue; long long sum=0; for(int co=1;co<=top;co++){ if(s[dy[i]][dy[co]])sum+=f[e[j].v][co]; } f[k][i]*=sum;if(!sum)break; } } } int main() { cin>>n>>m; for(int i=1;i<=m;i++){ scanf("%d%d",&t1,&t2); s[t1][t2]=s[t2][t1]=1; } for(int i=1;i<n;i++){ scanf("%d%d",&t1,&t2); lj(t1,t2);lj(t2,t1); } for(int i=1;i<=((1<<n)-1);i++) { top=0; for(int j=0;j<n;j++)if(i&(1<<j))dy[++top]=j+1; dfs(1,0); long long fs=0; for(int j=1;j<=top;j++)fs+=f[1][j]; if((n-top)%2==0)ans+=fs; else ans-=fs; } cout<<ans<<endl; return 0; }