bzoj4455:[Zjoi2016]小星星

传送门

考虑假如不考虑重复映射
那么显然可以得到一个\(O(n^3)\)的树形dp
然后考虑如何去掉不合法的情况?
容斥,考虑每次只能从一个点集\(S\)里选点(也就是至多\(|S|\)个点的映射的方案数)
那么显然就可以枚举点集\(S\),做树形dp,然后容斥一下就做完了
总复杂度:\(O(2^nn^3)\)
代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
void read(int &x){
    char ch;bool ok;
    for(ok=0,ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')ok=1;
    for(x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());if(ok)x=-x;
}
#define rg register
const int maxn=20;
int n,m,d[maxn][maxn],dd[maxn][maxn],t[maxn],num,tot;
long long f[maxn][maxn],ans;
void dfs(int x,int fa){
    for(rg int i=1;i<=num;i++)f[x][t[i]]=1;
    for(rg int i=1;i<=n;i++)
        if(i!=fa&&dd[x][i]){
            dfs(i,x);
            for(rg int j=1;j<=num;j++)
            {
                long long g=0;
                for(rg int k=1;k<=num;k++)if(d[t[j]][t[k]])g+=f[i][t[k]];
                f[x][t[j]]*=g;
            }
        }
}
int main()
{
    read(n),read(m),tot=1<<n;
    for(rg int i=1,x,y;i<=m;i++)read(x),read(y),d[x][y]=d[y][x]=1;
    for(rg int i=1,x,y;i<n;i++)read(x),read(y),dd[x][y]=dd[y][x]=1;
    for(rg int i=0;i<tot;i++){
        num=0;
        for(rg int j=0;j<n;j++)if(i&(1<<j))t[++num]=j+1;
        dfs(1,0);
        for(rg int j=1;j<=num;j++)ans+=((n-num)&1?-1ll:1ll)*f[1][t[j]];
    }
    printf("%lld\n",ans);
}
posted @ 2019-05-01 16:35 蒟蒻--lichenxi 阅读(...) 评论(...) 编辑 收藏