Codeforces Round #332 (Div. 2) E. Sandy and Nuts

http://codeforces.com/contest/599/problem/E

题意不再多说...

我用 dp[i][type][s] 表示以i为根,包含二进制s所表示的节点(不含i)的子树的方案数,type的作用放到后面解释。

那么答案就是 dp[0][0][(1<<n)-2]

状态转移的时候我先分离出当前sta最低位的1(lowbit), 然后我枚举和这一位组成一颗子树的情况(这样使得枚举不会重复,队友说的。。)。

对于情况s,要判断是否有效,我枚举以i为公共祖先的点对,要求不能同时在s里,并且对于每个s中的点,与它相连的点要在s中或者等于i。

对于符合条件的s, 枚举某个点j作为子树的根, dp[i][type][sta] += dp[j][0][s] * dp[i][1][sta -s].

type == 0 表示未被分割的情况,type== 1表示另外还有子树连着根。这是为了当type==0时,我要首先判断一下以i为公共祖先的点对要都在点集里。

递归结束条件是sta == 0 时 return 1。

(感觉并没有讲清楚T^T)


#include 
   
   
    
    

using namespace std;

typedef long long ll;
#define rep(i,s,t) for(int i=int(s); i
    
    
     
      pii;

int n,m,q;
int linked[15];
ll dp[15][3][1<<13+5];
pii mlca[15][105];
int cnt_lca[15];

bool judge(int root, int s, int mods)
{
	rep(i, 0, n) if((s >> i) & 1)
	{
		if((linked[i] | mods) != mods) return false;
	}
	rep(i, 0, cnt_lca[root])
	{
		if(((s >> mlca[root][i].first) & 1) && ((s >> mlca[root][i].second) & 1)) return false;
	}
	return true;
}
ll dpit(int root, int beg, int sta) // root 0 ~ n-1
{
	if(~dp[root][beg][sta]) return dp[root][beg][sta];
	if(beg == 0)
	{
		int tmps = sta + (1 << root);
		rep(i, 0, cnt_lca[root])
		{
			if(((tmps >> mlca[root][i].first) & 1) && ((tmps >> mlca[root][i].second) & 1)) continue;
			return dp[root][beg][sta] = 0;
		}
	}
	if(sta == 0)
	{
		return 1;
	}
	ll& ret = dp[root][beg][sta];
	ret = 0;
	int has = (sta & (-sta));
	sta -= has;
	for(int s = 0; s<=sta; s++) if((s | sta) == sta)
	{
		int ss = s + has;
		if(!judge(root, ss, ss + (1 << root))) continue;
		for(int i=0; i
     
     
      
      > i) & 1)
		{
			ret += dpit(i, 0, ss - (1 << i)) * dpit(root, 1, sta - s);
		}
	}
	//cout<
      
      
       
       <<" "<
       
       
         <<" "< 
        
          << b); linked[b] += (1 << a); } rep(i, 0, q) { scanf("%d%d%d", &a, &b, &c); a --, b --, c --; mlca[c][cnt_lca[c]++] = {a, b}; } mst(dp, -1); ll ans = dpit(0, 0, (1< 
          
         
       
      
      
     
     
    
    
   
   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值