[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;
}
原文地址:https://www.cnblogs.com/HocRiser/p/8678043.html