zoukankan      html  css  js  c++  java
  • BZOJ4455 ZJOI2016小星星(容斥原理+树形dp)

      相当于给树上的每个点分配一个编号使父亲和儿子间都有连边。

      于是可以考虑树形dp:设f[i][j][k]为i号点的编号为j,其子树中编号集合为k的方案数。转移显然。然而复杂度3n·n3左右,具体我也不知道是多少,但肯定跑不过。

      如果状态有集合的话不管怎样底数都是3了,考虑能不能变成2。完全不能可以想到容斥。

      于是在dp中去掉k这一维。那么dp变成n3的。但是dp显然会有问题,即会出现不同的点取了相同编号的情况。这也可以看做是有编号未被选择。

      那么就可以容斥了。先算出编号在全集中选择的答案,然后减去在全集去掉一个编号所得子集中选择的答案,再加上在全集去掉两个编号所得子集中选择的答案……通过组合数计算子集被计入的次数容易验证其正确性。

      枚举子集然后dp,复杂度就是O(2n·n3)了。注意卡常。

    #include<iostream> 
    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int read()
    {
        int x=0,f=1;char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
        while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return x*f;
    }
    #define N 18
    #define ll long long 
    int n,m,a[N][N],stk[N],p[N],t;
    ll f[N][N],ans=0;
    struct data{int to,nxt;
    }edge[N<<1];
    void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
    void dp(int k,int from,int s)
    {
        memset(f[k],0,sizeof(f[k]));
        for (int x=1;x<=s;x++) 
        f[k][stk[x]]=1;
        for (int i=p[k];i;i=edge[i].nxt)
        if (edge[i].to!=from)
        {
            dp(edge[i].to,k,s);
            for (int x=1;x<=s;x++)
            {
                ll sum=0;
                for (int y=1;y<=s;y++)
                if (a[stk[x]][stk[y]]) sum+=f[edge[i].to][stk[y]];
                f[k][stk[x]]=f[k][stk[x]]*sum;
            }
        }
    }
    void dfs(int k,int s)
    {
        if (k>n) 
        {
            dp(1,1,s);
            ll sum=0;
            for (int i=1;i<=n;i++) sum+=f[1][i];
            if (n-s&1) ans-=sum;else ans+=sum;
            return;
        }
        stk[s+1]=k;dfs(k+1,s+1);
        dfs(k+1,s);
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("bzoj4455.in","r",stdin);
        freopen("bzoj4455.out","w",stdout);
        const char LL[]="%I64d";
    #else
        const char LL[]="%lld";
    #endif
        n=read(),m=read();
        while (m--)
        {
            int x=read(),y=read();
            a[x][y]=a[y][x]=1;
        }
        for (int i=1;i<n;i++)
        {
            int x=read(),y=read();
            addedge(x,y),addedge(y,x);
        }
        dfs(1,0);
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    计算GPS WGS_84 两点的距离
    极路由4_开ssh_刷breed
    aes-256-gcm_python3_php7_golang
    nginx_非标准端口_同端口_http_自动跳转_https
    配置sshd_除了特定ip外_仅密钥登录
    使用scp命令实现服务器之间文件传输
    Java防止重复提
    mysql使用SUBSTRING_INDEX截取部分字符串
    SEO大杀器rendertron安装
    PIC16 bootloader之I2C bootloader
  • 原文地址:https://www.cnblogs.com/Gloid/p/9460913.html
Copyright © 2011-2022 走看看