zoukankan      html  css  js  c++  java
  • 洛谷 P3349 [ZJOI2016]小星星 解题报告

    P3349 [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)通过细线连了起来。保证这些小星星通过细线可以串在一起。

    输出格式:

    输出共(1)行,包含一个整数表示可能的对应方式的数量。如果不存在可行的对应方式则输出(0)

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


    说起来这个题我看了很久啊。。

    读题解也读了半天

    始终搞不懂暴力的(dp)的复杂度是怎么证明的

    题意:大致借用一下你谷题解的说法,求树(T)到图(G)的映射个数,要求点的映射一一对应,点引出的边在图中存在

    图中每个点都被映射一次是个麻烦点,这要求我们现有映射集合作为状态存下来

    于是这样暴力

    (dp_{i,j,s})代表(i)点映射为(j)点后,以(i)为子树的一个映射集合为(s)

    转移时枚举(S)的子集,然后一个子树一个子树的枚举过去(复杂度似乎是乘起来的。。

    判断一下有没有连边是否统计就可以了

    既然每个点被映射一次很麻烦,不如去掉这个鬼限制

    我们随便映射,只有恰好都映射的是合法的

    考虑先减掉图中一个点没有被映射的情况,然后加上两个点没有,....

    也就是说,我们把可以映射的点的每个子集的答案求出来,然后根据容斥原理加或者减就可以了

    每一次的映射集合我们都做一次树形dp

    复杂度:(O(n^32^n))


    Code:

    #include <cstdio>
    #define ll long long
    ll dp[18][18];
    int n,m,ha[18],g[18][18];
    int Next[50],head[18],to[50],cnt;
    void add(int u,int v)
    {
        to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt;
        to[++cnt]=u,Next[cnt]=head[v],head[v]=cnt;
    }
    void dfs(int now,int fa)
    {
        for(int i=head[now];i;i=Next[i])
        {
            int v=to[i];
            if(v!=fa) dfs(v,now);
        }
        for(int i=1;i<=ha[0];i++)
        {
            dp[now][i]=1;
            for(int j=head[now];j;j=Next[j])
            {
                int v=to[j];ll sum=0;
                if(v==fa) continue;
                for(int k=1;k<=ha[0];k++)
                    sum+=dp[v][k]*g[ha[i]][ha[k]];
                dp[now][i]*=sum;
            }
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int u,v,i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            g[u][v]=g[v][u]=1;
        }
        for(int u,v,i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            add(u,v);
        }
        ll ans=0;
        for(int s=1;s<1<<n;++s)
        {
            ha[0]=0;ll sum=0;
            for(int i=0;i<n;i++) if(s>>i&1) ha[++ha[0]]=i+1;
            dfs(1,0);
            for(int i=1;i<=ha[0];i++) sum+=dp[1][i];
            if(n-ha[0]&1) ans-=sum;
            else ans+=sum;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    
    

    2018.9.7

  • 相关阅读:
    是否需要代码规范
    上网调查一下目前流行的源程序版本管理软件和项目管理软件都有哪些?各有什么优缺点?
    作业二--------个人编程项目:四则运算。
    学习进度总结————王烁130201218
    作业一:建立博客、自我介绍、速读教材、学习进度总结
    论实习之后的感悟
    作业九 ——报告及总结
    作业四: 结对编程项目---四则运算
    代码复审
    PSP记录个人项目耗时情况
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9605884.html
Copyright © 2011-2022 走看看