zoukankan      html  css  js  c++  java
  • 【容斥】[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通过细线连了起来。 保证这些小星星通过细线可以串在一起。 n<=17,m<=n*(n-1)/2

    输出格式

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

    样例

    样例输入

    4 3
    1 2
    1 3
    1 4
    4 1
    4 2
    4 3

    样例输出

    6

    背景

      这个题真的是GG

      题目中一点暗示都没有,我还是凭常年wa题的经验才把 long long开起来的QAQ

      真是*****。

      考场上我WA了3次,第一次50,错因应该就是爆int。

      第二次我以为我的 tree DP 给wa掉了,改了个限制条件。

      第三次我终于意识到 long long 的事情了,但是我没改限制条件。

      第四次我终于AC了。

      QAQ


    思路分析

      首先,这个题,特别容易看错【如果不看样例的话绝对会理解出错,比如我

      我一看这个题,觉得是一个求生成树的问题。

      emmm或者说是通过生成树倒退原图的过程。

      就在我苦思冥想怎么做的时候,我瞄了一眼样例。

      QAQ 已经给出原图,给出生成树了。

      ??????什么鬼啊?????????

      回去看题面,发现里面有一句这样的话:

    她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。

      也就是说,剪掉一些线之后,星星的编号变了。

        ??????什么意思啊????????

      这个时候,我们发现其实每个节点究竟是什么已经不重要了。

      重要的是, 哪两个点之间有着连接的关系

      所以这题就变成了,给定一些连接关系,求满足下列关系的组合有多少个?

      那么,很显然,我们可以直接计算每个点映射之后会形成的方案数。【对于每个位置可选的方案数称起来就可以了

      考虑 dp[ i ][ j ] 在限制S下的含义为: 树中的点只能映射原图上属于集合 S 的点 ,i 点映射到 j 点 在以 i 为根的子树中的方案。

      这个时候,手玩两组数据,就会发现,多算了。

      在映射的时候,有可能把好多个节点都标号成了同一个。

      那么就需要把这些方案数减去。

      然后我就想到了容斥。【可能我本来容斥就学的不错,一下就想到了

      假设,在限制S下的映射方案数为ans[S] ,只许映射到S且S集合中每个点都被至少映射一次的方案数为 calc [S] 。

      那么显然的是 : ans[S] = Σ calc [T]  (其中T是S的一个子集)。

      所以我们容斥从小到大计算出calc,最后calc[2^n-1]就是答案。


    代码实现

      

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #define ll long long 
    #define rint register int
    using namespace std;
    
    ll n,m,num;
    ll a[20][20],c[30],cnt[20];
    ll f[20][20],ans[140005];//ans表示选点集合只能是S子集的方案数
    vector<ll> v[20];
    
    void dp(ll x,ll fa) {
        for(rint i=1;i<=num;i++) 
            f[x][i]=1;
        for(rint i=v[x].size()-1;i>=0;i--) {
            ll to=v[x][i];
            if(to!=fa) {
                dp(to,x);
                for(rint j=1;j<=num;j++) {
                    ll nw=0;
                    for(rint k=1;k<=num;k++) 
                        if(a[cnt[j]][cnt[k]]) 
                            nw+=f[to][k];
                    f[x][j]*=nw;
                }
            }
        }
        return;
    }
    
    int main()
    {
        scanf("%lld%lld",&n,&m);
        for(rint i=1;i<=m;i++) {
            ll x,y;
            scanf("%lld%lld",&x,&y);
            a[x][y]=1;
            a[y][x]=1;
        }
        for(rint i=1;i<n;i++) {
            ll x,y;
            scanf("%lld%lld",&x,&y);
            v[x].push_back(y);
            v[y].push_back(x);
        }
        c[0]=1;
        for(rint i=1;i<=20;i++) 
            c[i]=c[i-1]<<1;
        for(rint i=1;i<c[n];i++) {
            num=0;
            memset(f,0,sizeof f);
            for(rint j=1;j<=n;j++)
                if(i&c[j-1]) 
                    cnt[++num]=j;
            dp(1,1);
            for(rint j=1;j<=num;j++)
                ans[i]+=f[1][j];
        }
        for(rint i=1;i<c[n];i++) 
            for(rint j=(i-1)&i;j;j=(j-1)&i)
                ans[i]-=ans[j];
        cout<<ans[c[n]-1]<<endl;
        return 0;
    }

    生气了,所以全部Longlong!!!!!!

  • 相关阅读:
    反转链表 16
    CodeForces 701A Cards
    hdu 1087 Super Jumping! Jumping! Jumping!(动态规划)
    hdu 1241 Oil Deposits(水一发,自我的DFS)
    CodeForces 703B(容斥定理)
    poj 1067 取石子游戏(威佐夫博奕(Wythoff Game))
    ACM 马拦过河卒(动态规划)
    hdu 1005 Number Sequence
    51nod 1170 1770 数数字(数学技巧)
    hdu 2160 母猪的故事(睡前随机水一发)(斐波那契数列)
  • 原文地址:https://www.cnblogs.com/qxyzili--24/p/11234294.html
Copyright © 2011-2022 走看看