zoukankan      html  css  js  c++  java
  • 【BZOJ4455】小星星(动态规划,容斥)

    【BZOJ4455】小星星(动态规划,容斥)

    题面

    BZOJ
    洛谷
    Uoj

    题解

    题意说简单点就是给定一张(n)个点的图和一棵(n)个点的树,现在要让图和树之间的点一一对应,并且如果树上存在一条边,那么图上对应的点对之间也要存在边。

    我们直接求解显然很麻烦,一一对应是一个很不好算的东西。
    那么我们先要求并不需要一一对应,随意对应即可,最后再减掉不合法的方案,这样就可以用容斥来解决。
    怎么容斥呢?无非是考虑没有一一对应的关系,那么我们先暴力枚举一下哪些点在图上可以和树上的点进行对应,其他的点不能够和树上的点进行匹配。
    那么考虑(dp)计算方案数。
    (f[i][j])表示当前以(i)为根的子树(假装以(1)号点为根节点的有根树),并且(i)在图上对应的点是(j)的方案数。
    每次暴力选择一个和当前(i)匹配的点,然后再暴力找到这个点在图中的所有儿子,并且用子树进行转移,这样(dp)一次的复杂度是(O(n imes n imes n)),即树上每个点都要做一次,要暴力枚举和哪个点进行匹配,还需要暴力枚举儿子是哪个点,当然这样肯定不满。
    再加上暴力枚举可以进行匹配的点集的枚举,
    所以总的时间复杂度是(O(n^32^n))

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    #define ll long long
    #define MAX 20
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
        while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
        if(ch=='-')t=true,ch=getchar();
        while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
        return t?-x:x;
    }
    struct Line{int v,next;}e1[MAX*MAX<<1],e2[MAX<<1];
    int h1[MAX],h2[MAX];
    int cnt1=1,cnt2=1;
    int n,m,cnt,a[MAX];
    inline void Add1(int u,int v){e1[cnt1]=(Line){v,h1[u]};h1[u]=cnt1++;}
    inline void Add2(int u,int v){e2[cnt2]=(Line){v,h2[u]};h2[u]=cnt2++;}
    ll f[MAX][MAX],ans=0,num;
    void dfs(int u,int ff)
    {
    	for(int i=h2[u];i;i=e2[i].next)
    		if(e2[i].v!=ff)dfs(e2[i].v,u);
    	for(int i=1;i<=cnt;++i)//枚举当前点得到匹配点
    	{
    		f[u][a[i]]=1;
    		for(int j=h2[u];j;j=e2[j].next)
    			if(e2[j].v!=ff)
    			{
    				num=0;
    				for(int k=h1[a[i]];k;k=e1[k].next)num+=f[e2[j].v][e1[k].v];
    				f[u][a[i]]*=num;
    				if(!f[u][a[i]])break;
    			}
    	}
    }
    int main()
    {
    	n=read();m=read();
    	for(int i=1,u,v;i<=m;++i)u=read(),v=read(),Add1(u,v),Add1(v,u);
    	for(int i=2,u,v;i<=n;++i)u=read(),v=read(),Add2(u,v),Add2(v,u);
    	for(int i=1;i<(1<<n);++i)//枚举可以进行匹配的点集
    	{
    		cnt=0;memset(f,0,sizeof(f));
    		for(int j=0;j<n;++j)if(i&(1<<j))a[++cnt]=j+1;
    		dfs(1,0);num=0;
    		for(int j=1;j<=cnt;++j)num+=f[1][a[j]];
    		if((n-cnt)&1)ans-=num;else ans+=num;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
    
  • 相关阅读:
    International Collegiate Programming Contest 2019 Latin American Regional Contests E. Eggfruit Cake(思维/尺取)
    Codeforces Round #673 (Div. 2) C. k-Amazing Numbers(思维)
    2020 计蒜之道 预赛 第一场 A、B
    生成字符画
    我对目前国内教学的看法
    Stm32 调试时发生HardFault_Handler
    python异常处理
    windows nfs客户端配置
    linux服务器删除文件后df -h查看文件系统占比无变化
    python ssh小程序
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9386022.html
Copyright © 2011-2022 走看看