• [BZOJ4455][ZJOI2016]数星星(容斥DP)


    4455: [Zjoi2016]小星星

    Time Limit: 10 Sec  Memory Limit: 512 MB
    Submit: 707  Solved: 419
    [Submit][Status][Discuss]

    Description

    小Y是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有n颗小星星,用m条彩色的细线串了起来,每条细
    线连着两颗小星星。有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了n?1条细线,但
    通过这些细线,这颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。小Y找到了这个饰品的设
    计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,
    那么要求对应的小星星原来的图纸上也有细线相连。小Y想知道有多少种可能的对应方式。只有你告诉了她正确的
    答案,她才会把小饰品做为礼物送给你呢。

    Input

    第一行包含个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

    Output

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

    Sample Input

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

    Sample Output

    6

    HINT

    Source

    一个很好理解的题解:https://blog.csdn.net/johann_oier/article/details/51090513

    思路就是:如果集合可重,那么我们对于以i为根的子树,直接枚举这些节点的集合,然后f[i][j]表示i映射到原图的j的方案数。

    那么现在集合不可重怎么办呢?去掉这个限制的一个好办法就是容斥,显然最后整棵树对应的集合是全集,那么我们将映射集合为全集的方案数-映射集合大小为n-1的方案数+大小为n-2的方案数-...,最后就是答案了。

    #include<cstdio>
    #include<algorithm>
    #define rep(i,l,r) for (int i=(l); i<=(r); i++)
    typedef long long ll;
    using namespace std;
    
    const int N=20;
    ll f[N][N];
    int n,m,ed,x,y,cnt,hash[N],g[N][N],mp[N][N],fa[N],p[N],q[N],h[N],to[N],nxt[N];
    void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
    
    void bfs(int s){
        int l=1,r=1; q[1]=s;
        while (l<=r){
            int x=q[l],c=0; l++;
            rep(i,1,n) if (mp[x][i] && i!=fa[x]) q[++r]=i,fa[i]=x,++c,add(x,i);
            if (!c) hash[x]=1;
        }
    }
    
    void dp(int s,int sta,int cnt){
        for (int i=n; i; i--){
            int x=q[i];
            if (hash[x]) continue;
            rep(j,1,cnt){
                for (int i=h[x],k; i; i=nxt[i])
                    if (fa[k=to[i]]==x){
                        ll sm=0;
                        rep(l,1,cnt) if (g[p[j]][p[l]]) sm+=f[k][l];
                        f[x][j]*=sm;
                    }
            }
        }
    }
    
    int get(int x){
        int cnt=0;
        for (int j=1; x; x>>=1,j++) if (x & 1) p[++cnt]=j; x>>=1;
        return cnt;
    }
    
    int main(){
        freopen("bzoj4455.in","r",stdin);
        freopen("bzoj4455.out","w",stdout);
        scanf("%d%d",&n,&m);
        rep(i,1,n) g[i][i]=1;
        rep(i,1,m) scanf("%d%d",&x,&y),g[x][y]=g[y][x]=1;
        rep(i,1,n-1) scanf("%d%d",&x,&y),mp[x][y]=mp[y][x]=1;
        bfs(1); ll ans=0; int tag=n&1;
        for (int sta=1; sta<(1<<n); sta++){
            int cnt=get(sta);
            ll flag=((cnt&1)==tag)?1:-1,s=0;
            rep(i,1,n) rep(j,1,cnt) f[i][j]=1;
            dp(1,sta,cnt);
            rep(i,1,cnt) s+=f[1][i];
            ans+=flag*s;
        }
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    特殊符号大全
    CSS速记
    JavaScript命名规范
    vim 常用快捷键
    Set a mouse hook
    ASP不可遗弃的通用函数
    ASP递归层级表格数据展示
    最简约JS日历控件
    ASP统计图
    ASP标准MD5代码
  • 原文地址:https://www.cnblogs.com/HocRiser/p/8678043.html
Copyright © 2020-2023  润新知