zoukankan      html  css  js  c++  java
  • [ZJOI2016]小星星

    洛谷题目链接

    动态规划$+$容斥原理:

    1、暴力状压

    我们尝试设计状态:$$f[i][j][S]$$

    表示新的图上点$i$对应旧的图上点$j$并且所有点的状态为一个二进制数$S$时的方案数

    那么只需要暴力树形$dp$就行了

    但是这样做为什么说是暴力呢。。。一看就知道,复杂度爆炸,然而我并不会证$qwq$

    2、正解(可能吧):容斥原理

    我们思考上面这种方法为什么会爆炸,就是因为有了最后那维状态,如果我们可以省去那个状态的话,就可以降低时间复杂度了,于是我们设:

    $$f[i][j]$$表示新的图上点$i$对应旧的图上点$j$时的方案数

    那么我们就面临一个问题,我们只需要$n$个点在集合内的方案数,但是$n-1,n-2,cdots$的点的方案数我们都算进来了

    那么我们算一下$n-1$的答案,这些方案数包括$n-2,n-3,cdots$等的方案数

    但是我们发现我们总共减了两次$n-2$的方案数,所以再加上$n-2$的答案,但是又多了$n-3$等,我们发现:一加一减到最后刚好就是我们要的$n$个点的方案数

    所以:$|S|=n$时的方案数减去$|S|=n-1$时的方案数加上$|S|=n-2$时的方案数减去$|S|=n-3$时的方案数$......$

    一加一减就阔以了

    接下来是美滋滋的代码时间~~~

    #include<iostream>
    #include<cstdio>
    #include<cstring> 
    #define ll long long
    #define N 19
    #define M 140
    using namespace std;
    struct Edge
    {
    	int to,nxt;
    }edge[N<<1];
    int n,m,cnt;
    ll ans;
    int ban[N],head[N],g[N][N];
    ll f[N][N];
    void Add(int u,int v)
    {
    	edge[++cnt]=(Edge){v,head[u]};
    	head[u]=cnt;
    }
    void Dfs(int u,int fa)
    {
    	for(int i=1;i<=n;++i)
    		f[u][i]=1;
    	for(int i=head[u];i;i=edge[i].nxt)
    	{
    		int v=edge[i].to;
    		if(v==fa)
    			continue;
    		Dfs(v,u);
    		for(int j=1;j<=n;++j)
    		{
    			ll sum=0;
    			for(int k=1;k<=n;++k)
    				sum+=f[v][k]*(g[k][j]&&ban[j]&&ban[k]);
    			f[u][j]*=sum;
    		}
    	}
    }
    signed main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;++i)
    	{
    		int u,v;
    		scanf("%d%d",&u,&v);
    		g[u][v]=g[v][u]=1;
    	}
    	for(int i=1;i<n;++i)
    	{
    		int u,v;
    		scanf("%d%d",&u,&v);
    		Add(u,v);
    		Add(v,u);
    	}
    	for(int i=0;i<(1<<n);++i)
    	{
    		memset(ban,0,sizeof(ban));
    		ll ret=0;
    		int size=n,now=i;
    		for(int j=1;now;now>>=1,++j)
    			ban[j]=(now&1),size-=(now&1);
    	//	for(int j=1;j<=n;++j)
    	//		printf("%d ",ban[j]);
    	//	printf("
    ");
    		Dfs(1,0);
    		for(int j=1;j<=n;++j)
    			ret+=f[1][j];
    		if(size%2)
    			ans-=ret;//printf("%d
    ",ans);
    		else
    			ans+=ret;
    	}
    	printf("%lld",ans);
    	return 0;
    }
    

      

  • 相关阅读:
    C语言(1)
    ​ Markdown
    多功能嵌入式解码软件(4)
    多功能嵌入式解码软件(3)
    多功能嵌入式解码软件(2)
    STM32最小系统设计
    C#通过字符串分割字符串Split
    基于串口的SD_card系统
    直流无刷电机工作原理
    Java常用函数式接口--Consumer接口使用案例
  • 原文地址:https://www.cnblogs.com/yexinqwq/p/10254401.html
Copyright © 2011-2022 走看看