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

  • 相关阅读:
    5.4 省选模拟赛 修改 线段树优化dp 线段树上二分
    一本通 高手训练 1782 分层图 状压dp
    luogu P3830 [SHOI2012]随机树 期望 dp
    5.2 省选模拟赛 或许 线型基
    luogu P4562 [JXOI2018]游戏 组合数学
    一本通 高手训练 1781 死亡之树 状态压缩dp
    luogu P4726 【模板】多项式指数函数 多项式 exp 牛顿迭代 泰勒展开
    4.28 省选模拟赛 负环 倍增 矩阵乘法 dp
    HDU 1756 Cupid's Arrow 计算几何 判断一个点是否在多边形内
    一本通 高手训练 1763 简单树 可持久化线段树 树链刨分 标记永久化
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9605884.html
Copyright © 2011-2022 走看看