AGC 016 F - Games on DAG(状压dp)

本文解析了一道复杂的状压动态规划题目,AGC016F-GamesonDAG,涉及DAG图上的两人博弈策略。通过计算SG值并运用补集转化,解决了先手必胜的边集数量问题,提供了详细的算法实现思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AGC 016 F - Games on DAG(状压dp)

题意

给你一个有 n 个点 m 条边 DAG 图,点的标号和拓扑序一致。

现在有两个人进行博弈,有两个棋子分别在 1,2 号点上,需要不断移动到它指向的点上。

如果当前两个点都无法移动,那么就视为当前操作的人失败。

问有多少边集满足先手必胜。

\displaystyle 2 \le n \le 15, m \le \frac{n \times (n+1)}{2}

 

这是一道思维非常困难的状压题

我们考虑转化问题,两个棋子是独立的,那么我们如果求出了这两个棋子的SG值

最后就是求两个棋子SG值不相同的方案数

我们考虑补集转化,现在要求两个棋子SG值相同的方案数

dp[s]表示选的点的状态为s时,总共的方案数

我们要让1,2个点sg为0,所以要么一起选1,2,要么一起不选

然后我们可以枚举子集ss,子集的sg全为0,x^ss全部不为0

那么把x^ss全都连到ss上面,他们的sg函数全部加1

这样无论何时1,2两个点就都是sg函数相同的了

最后dp[(1<<n)]就是答案了

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int n,m,shu1,shu2,point[1000005],dp[1000005];
int ans,num[1000005];
int moc(int x)
{
	if(x>=mod) return x-mod;
	if(x<0) return x+mod;
	return x;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&shu1,&shu2);
		shu1--,shu2--;
		point[shu1]|=1<<shu2;
	}
	int maxa=(1<<n)-1;
	dp[0]=1;
	for(int i=0;i<=maxa;i++)
	{
		int now=0;
		for(int j=0;j<n;j++)
		if(i>>j&1) now++;
		num[i]=1<<now;
	}
	for(int s=1;s<=maxa;s++)
	if((s&1)==(s>>1&1))
	{
		for(int ss=s;ss;ss=(ss-1)&s)
		if((ss&1)==(ss>>1&1))
		{
			int t=s^ss,now=1;
			for(int i=0;i<n;i++)
			if(ss>>i&1)
			now=1ll*now*num[point[i]&t]%mod;
			else if(t>>i&1)now=1ll*now*(num[point[i]&ss]-1)%mod;
			dp[s]=moc(dp[s]+1ll*now*dp[t]%mod);
		}
	}
	ans=1;
	for(int i=1;i<=m;i++)
	ans=moc(ans<<1);
	ans=moc(ans-dp[maxa]);
	printf("%d",ans);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值