bzoj4455 [Zjoi2016]小星星

传送门

__debug神仙计数课件的容斥入门题,有丶遭不住。

首先根据题目可以想出一个错误的treedp的做法,设\(f[u][x]\)表示以\(u\)为根的这棵子树中,以\(u\)为根,对应原图的\(x\)点的方案数,那么我们可以得到一个\(O(n^3)\)的转移方法(枚举每个儿子以及其对应的点),但是这样需要去重,因为多个点可能在原图对应的点相同。然后考虑容斥,我们\(2^n\)枚举每个子集可以是哪些点被填,然后考虑每个子集会重复的部分,拿\(<=n\)个点的\(\sum_{i=1}^{n}f[1][i]\)减去\(<=n-1\)个点的加上\(<=n-2\)个点的...以此类推,画画韦恩图就很好理解了。

然后这题卡常,fuck出题人

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int N=20;
ll ans,f[N][N];
int g[N][N],t[N][N],s[N],n,m,tot;
void dfs(int u,int fa){
    for(int i=1;i<=tot;i++)
        f[u][s[i]]=1;
    for(int i=1;i<=n;i++){
        if(!t[u][i]||i==fa)continue;
        dfs(i,u);
        for(int j=1;j<=tot;j++){
            ll sum=0;int x=s[j];
            for(int k=1;k<=tot;k++)
                if(g[x][s[k]])sum+=f[i][s[k]];
            f[u][x]*=sum;
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1,u,v;i<=m;i++)scanf("%d%d",&u,&v),g[u][v]=g[v][u]=1;
    for(int i=1,u,v;i<n;i++)scanf("%d%d",&u,&v),t[u][v]=t[v][u]=1;
    for(int S=0;S<(1<<n);S++){
        tot=0;
        for(int i=0;i<n;i++)if(S&(1<<i))s[++tot]=i+1;
        dfs(1,0);
        for(int i=1;i<=tot;i++)
            ans+=((n-tot)&1?-1:1)*f[1][s[i]];
    }
    printf("%lld\n",ans);
}

posted @ 2019-05-04 20:08  努力进步的肥宅yxc  阅读(150)  评论(0编辑  收藏  举报