• 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
    ",ans);
        return 0;
    }
  • 相关阅读:
    Ubuntu换源
    如何查看已购买的office密钥
    python的模块放在哪里
    centos7不能启动网卡报No suitable device found for this connection错误
    搭建邮件服务器 使用Postfix与Dovecot
    SSH远程免密码的密钥登录服务(Linux,Linux)
    SSH服务搭建、账号密码登录远程Linux虚拟机、基于密钥的安全验证(Windows_Xshell,Linux)
    apache基础,apache环境搭建,apache的3种使用方式(IP、端口、域名)
    Linux vsftpd服务配置以及三种验证方式以及常见错误解决办法
    使用DHCP动态管理主机地址
  • 原文地址:https://www.cnblogs.com/zhangleo/p/11061051.html
Copyright © 2020-2023  润新知