• 【BZOJ4455】小星星(ZJOI2016)-树形DP+容斥原理


    测试地址:小星星
    做法:本题需要用到树形DP+容斥原理。
    我省省队队长Mychael曰:假紫题,水题。orz。
    这题要求的是,对一棵树上每个点求一个映射,使得每一条树边在映射到一个图上后仍存在,求方案数。容易想到以下状态定义:
    dp(i,j,k)为以i为根的子树,i映射到j,并且子树中的点映射到的点集为k的方案数。
    然后就可以转移了。然而这个DP是O(n33n)的,无法通过此题,所以我们要想一个新的办法。
    注意到我们把k一维略掉后,求出的方案数是没有“两个不同的点映射到的点不同”这一限制的方案数。这个条件实际上等价于原图中的每个点都要被映射到,于是想到容斥,枚举一个点集并强制这个点集内的点不能被映射到,然后做一遍上面的DP,按照容斥的式子计算即可。这样时间复杂度就是O(n32n)的,开O2可以过掉此题。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,m,first[20]={0},firstt[20]={0},tot=0;
    ll dp[20][20];
    struct edge
    {
        int v,next;
    }e[1000],t[1000];
    void insert(int a,int b) {e[++tot].v=b,e[tot].next=first[a],first[a]=tot;}
    void insertt(int a,int b) {t[++tot].v=b,t[tot].next=firstt[a],firstt[a]=tot;}
    
    void DP(int now,int v,int fa)
    {
        for(int i=firstt[v];i;i=t[i].next)
            if (t[i].v!=fa) DP(now,t[i].v,v);
        for(int i=1;i<=n;i++)
            if (now&(1<<(i-1)))
            {
                dp[v][i]=1;
                for(int p=firstt[v];p;p=t[p].next)
                    if (t[p].v!=fa)
                    {
                        ll sum=0;
                        for(int q=first[i];q;q=e[q].next)
                            if (now&(1<<(e[q].v-1))) sum+=dp[t[p].v][e[q].v];
                        dp[v][i]*=sum;
                    }
            }
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            insert(a,b),insert(b,a);
        }
        tot=0;
        for(int i=1;i<n;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            insertt(a,b),insertt(b,a);
        }
    
        ll ans=0;
        for(int i=1;i<(1<<n);i++)
        {
            DP(i,1,0);
            int x=i,tot=0;
            while(x)
            {
                if (x&1) tot++;
                x>>=1;
            }
            ll f=1;
            if ((n-tot)%2) f=-1;
            for(int j=1;j<=n;j++)
                if (i&(1<<(j-1))) ans+=f*dp[1][j];
        }
        printf("%lld",ans);
    
        return 0;
    }
  • 相关阅读:
    笔记-迎难而上之Java基础进阶4
    笔记-迎难而上之Java基础进阶3
    笔记-迎难而上之Java基础进阶1
    7天学完Java基础之7/7
    Java学习笔记(3)--- 内部类,基本数据类型
    C++ 基础语法 快速复习笔记(3)---重载函数,多态,虚函数
    C++ 基础语法 快速复习笔记---面对对象编程(2)
    C++ 基础语法 快速复习笔记(1)
    堆与栈(heap and stack)在c/c++的应用(概念)
    Python爬虫入门教程 5-100 27270图片爬取
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793375.html
Copyright © 2020-2023  润新知