• [ZJOI2016]小星星


    此题解只是详细一些,推荐大家先看一个更好的:题解 P3349 【[ZJOI2016]小星星】

    就是该程序较机(gui)智(chu)。

    题目大意:

    小Y是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有n颗小星星,用m条彩色的细线串了起来,每条细线连着两颗小星星。

    有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了n-1条细线,但通过这些细线,这颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。小Y找到了这个饰品的设计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,那么要求对应的小星星原来的图纸上也有细线相连。小Y想知道有多少种可能的对应方式。

    n<=17,m<=n*(n-1)/2

    题目翻译:

    给定一个树,用这个树覆盖一个图。求方案数。树上不同的点对应图上不同的点,就是不同的方案。

    要求:

    1.树上一个点对应图上一个点,一对一的关系。

    2.树上相邻两个点,其对应于图上的点之间必须有连线。

    换句话说,求符合要求2的树上点对于图上点壹壹映射的方案数。

    分析:

    又是一道树形dp题。

    发现对于这个要求是很难满足的,尤其是一一对应。要是要记录一一对应的话,每个方案数还得记录子树中都选了哪些点,太麻烦了。

    发现,如果这些对应点可以随便选,dp起来还是很容易的。

    所以就让它先随便选,再去重。

    也就是说,先找出,随便瞎选的映射的方案数(两个及以上不同的点,也可以映射同一个点)

    去重,需要先减掉所有仅一个不选的方案数,也就是说,这是总数中不选i的方案数,也对应一个一定不合法的方案数。

    但是发现,同时不选i,j的方案数被减了两次。因为,我们没有限制必须选择除了i的剩下所有的,只是限制了不能选i,所以减掉i的时候,就把i,j都不选的情况减了一次。j的时候又一次。

    所以要加上同时不选两个的方案数。

    同理,要减去不选三个的,加上不选四个的...

    这,就是容斥原理。

    具体做法:

    用邻接矩阵存原图连通性,邻接表存树边。

    我们令f[i][j]表示,在i所在的子树中,i映射到原图中的j的方案数。

    对于x的一个子树y,回溯后,我们两遍循环,当f[x][i],f[y][j]时,原图中,i,j有连边的时候,就可以从f[y][j]转移到f[x][i].

    当然,这里i,j都是在我们该次dfs枚举的范围之内的。

    而,我们剩下的只需要枚举每次选择了哪几个数(剩下的就是不选的),这是2^n的,n<=17,可以承受。

    每次枚举完,树形dp一次,n^3。

    总复杂度:O(2^n*n^3)

    详见代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=20;
    ll f[N][N];
    int tot,zhan[N];
    ll ans;
    int n,m;
    struct node{
        int nxt,to;
    }bian[2*N];
    int hd[N],cnt;
    bool con[N][N];
    void add(int x,int y)
    {
        bian[++cnt].nxt=hd[x];
        bian[cnt].to=y;
        hd[x]=cnt;
    }
    void dp(int x,int fa)
    {
        for(int i=1;i<=tot;i++) f[x][zhan[i]]=1;//首先,每个点映射zhan[i]都有至少一种方案。 
        for(int t=hd[x];t;t=bian[t].nxt)
        {
            int y=bian[t].to;
            if(y==fa) continue;
            dp(y,x);
            for(int i=1;i<=tot;i++)
            {
                ll sum=0;
                for(int j=1;j<=tot;j++)
                {
                    if(con[zhan[i]][zhan[j]])//如果在原图中两个点相连 
                    {
                        sum+=f[y][zhan[j]];//这里其实是一个乘法分配律,每一种和原来方案的乘法原理做乘相加,等于所有的y的种类和,再相乘 
                    }
                }
                f[x][zhan[i]]*=sum;//这里,乘法原理,相当于之前所有的数的方案,与这个新儿子的所有方案进行不互相影响的组合。 
            }
        }
    }
    void dfs(int x,int has)//要选第x个数,选了has个数。 
    {
        if(x>n)//一个新的选择情况 
        {
        dp(1,-1);
        ll sum=0;
        for(int i=1;i<=tot;i++) sum+=f[1][zhan[i]];
        if((n-has)%2) ans-=sum;//不选奇数个,减去。 
        else ans+=sum; //不选偶数个,加上。 
        return;
        }
        tot++;zhan[tot]=x;dfs(x+1,has+1);//选此数 
        tot--;dfs(x+1,has);//不选 
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        int x,y;
        for(int i=1;i<=n;i++) con[i][i]=1;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            con[x][y]=1;con[y][x]=1;
        }//邻接矩阵 
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);add(y,x);
        }//邻接表 
        dfs(1,0);
        printf("%lld",ans);
        return 0;
    }
  • 相关阅读:
    tensorflow打印输出张量以及tf.one_hot()函数简介
    Windows10下Jupyter Notebook 使用虚拟环境
    图解Windows10下如何更换Jupyter Notebook 内核Python版本(切换原始的python环境)
    听说你在找工作?这里有很多招聘信息~
    数据集难找?GAN生成你想要的数据!!!
    妈妈再也不担心我没壁纸了!
    你的爬虫ip又被封了?教你一招
    飞鸽内网穿透
    第九届全国生物信息学与系统生物学学术大会
    SAP UI5 Routing 路由介绍
  • 原文地址:https://www.cnblogs.com/Miracevin/p/9048272.html
Copyright © 2020-2023  润新知