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;
}
View Code

 

posted @ 2020-02-14 19:40  liankewei123456  阅读(119)  评论(0编辑  收藏  举报