bzoj 4455

容斥好题

首先我们考虑,如果没有节点之间一一对应的限制,我们可以这样$dp$:

设状态$dp[i][j]$表示以$i$为根节点的子树,节点$i$与节点$j$对应的方案数

那么转移就是$dp[i][j]=\prod_{son_{i}}\sum_{k=1}^{n}map[j][k]dp[son_{i}][k]$

即枚举每个子节点与哪个点相对应,保证父子节点之间有连边

这个$dp$的时间复杂度是$O(n^{3})$的

但是有个问题,这样显然会产生大量的重复,因为会包括进去多个点对应同一个点的情况

那么我们考虑容斥:

有多个点对应同一个点等价于有一部分点没有点对应

因此我们枚举那一部分点没有被对应,然后做个容斥即可,容斥系数为$-1$的选出点集大小次幂

每次枚举出一个点集之后都需要重新进行$dp$,$dp$过程中保证不使用到不允许用的点即可

时间复杂度$O(2^{n}n^{3})$,bzoj严重卡常,需要足够强的卡常技巧

贴代码:

#include <cstdio>
#define ll unsigned long long
#define uint unsigned int
struct Edge
{
    uint next;
    uint to;
}edge[37];
uint head[18];
uint cnt=1;
uint my_stack[18];
uint ttop=0;
inline void add(uint l,uint r)
{
    edge[cnt].next=head[l];
    edge[cnt].to=r;
    head[l]=cnt++;
}
ll dp[18][18];
uint acc[18][18];
uint n,m;
void dfs(uint x,uint fx)
{
    for(register uint i=1;i<=ttop;++i)dp[x][my_stack[i]]=1;
    uint las=0;
    for(register uint i=head[x];i;i=edge[i].next)
    {
        uint to=edge[i].to;
        if(to==fx)
        {
            if(las)edge[las].next=edge[i].next;
            else head[x]=edge[i].next;
            continue;
        }
        las=i;
        dfs(to,x);
        for(register uint j=1;j<=ttop;++j)
        {
            ll sum=0;        
            for(register uint k=1;k<=ttop;++k)if(acc[my_stack[j]][my_stack[k]])sum+=dp[to][my_stack[k]];
            dp[x][my_stack[j]]*=sum;
        }
    }
}
inline uint read()
{
    uint f=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int main()
{
    n=read(),m=read();
    for(register uint i=1;i<=m;++i)
    {
        uint x=read(),y=read();
        acc[x][y]=acc[y][x]=1;
    }
    for(register uint i=1;i<n;++i)
    {
        uint x=read(),y=read();
        add(x,y),add(y,x);
    }
    ll ans=0;
    for(register uint i=0;i<(1<<n)-1;++i)
    {
        ttop=0;
        int x=1;
        for(register uint j=1;j<=n;++j)
        {
            if((1<<(j-1))&i)x=-x;
            else my_stack[++ttop]=j;
        }
        dfs(1,1);
        for(register uint j=1;j<=ttop;++j)
        {
            if(x==-1)ans-=dp[1][my_stack[j]];
            else ans+=dp[1][my_stack[j]];
        }
    }
    printf("%llu\n",ans);
    return 0;
}

 

posted @ 2019-06-20 20:06  lleozhang  Views(215)  Comments(0Edit  收藏  举报
levels of contents