Atcoder Grand Contest 016 F - Games on DAG(状压 dp)

洛谷题面传送门 & Atcoder 题面传送门

如何看待 tzc 补他一个月前做的题目的题解

首先根据 SG 定理先手必输当且仅当 \(\text{SG}(1)=\text{SG}(2)\)。考虑从反面入手,拿总情况数减去 \(\text{SG}(1)=\text{SG}(2)\) 的方案数。

怎么求 \(\text{SG}(1)=\text{SG}(2)\) 的方案数呢?看到这类数据范围巨小并且要你求“有多少组边集满足保留边集中的边后符合 xxx 条件”的题目,果断选择对点集进行状压。具体来说记 \(dp_S\) 为有多少种选择连接 \(S\) 中边的方法,使得 \(1,2\) 的 SG 值相同。考虑怎么转移。分两种情况,\(1,2\) SG 值都为 \(0\) 的情况还有 \(1,2\) SG 值均不为 \(0\) 的情况:

  • \(1,2\) SG 值都为 \(0\)。那么我们枚举 SG 值不为 \(0\) 的子集 \(T\),令 \(T'=S\text{\\}T\),那么显然不能存在 \(T\) 内部的边,同时由于 \(T'\) 中所有点的 SG 值均非零,所有 \(\forall x\in T'\),至少保留了一条 \(x\)\(T\) 中节点的边,而 \(T'\) 内部及 \(T\to T'\) 的边随便连,乘法原理算一下即可。
  • \(1,2\) SG 值都非零。那么我们还是枚举 SG 值不为 \(0\) 的子集 \(T\),令 \(T'=S\text{\\}T\),那么还是不能存在 \(T\) 内部的边,对于所有 \(\forall x\in T'\),也必须至少保留了一条 \(x\)\(T\) 中节点的边。与上一种情况的不同之处在于 \(T'\) 内部的边不能随便连了。如果我们把 SG 值为 \(0\) 的点去掉那么剩余的 DAG 中 \(1,2\) 的 SG 值也应相同,而根据 \(dp\) 状态的设计可知方案数为 \(dp_{T'}\)

时间复杂度 \(3^nn\)

const int MAXN=15;
const int MAXP=32768;
const int MAXM=105;
const int MOD=1e9+7;
int n,m,cnt[MAXN+5][MAXN+5],num[MAXP+5][MAXN+5],pw2[MAXM+5],dp[MAXP+5],ed[MAXP+5];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=(pw2[0]=1);i<=m;i++) pw2[i]=(pw2[i-1]<<1)%MOD;
	for(int i=1,u,v;i<=m;i++) scanf("%d%d",&u,&v),cnt[u][v]++,ed[(1<<u-1)|(1<<v-1)]++;
	for(int i=1;i<=n;i++) for(int j=0;j<(1<<n);j++) if(j>>(i-1)&1) ed[j]+=ed[j^(1<<i-1)];
	for(int i=0;i<(1<<n);i++) for(int j=1;j<=n;j++)
		for(int k=1;k<=n;k++) if(i>>(k-1)&1) num[i][j]+=cnt[j][k];
	for(int i=3;i<(1<<n);i++) if((i&3)==3){
		dp[i]=1;
		for(int j=i&(i-1);j;--j&=i) if((j&1)==((j>>1)&1)){
			int mul=1;
			for(int k=1;k<=n;k++) if(i>>(k-1)&1){
				if(j>>(k-1)&1) mul=1ll*mul*(pw2[num[i^j][k]]-1)%MOD;
				else mul=1ll*mul*pw2[num[j][k]]%MOD;
			} //printf("%d %d %d\n",i,j,mul);
			if(j&1) dp[i]=(dp[i]+1ll*dp[j]*mul)%MOD;
			else dp[i]=(dp[i]+1ll*mul*pw2[ed[j]])%MOD;
		}
	} printf("%d\n",(pw2[m]-dp[(1<<n)-1]+MOD)%MOD);
	return 0;
}
posted @ 2021-09-27 22:48  tzc_wk  阅读(37)  评论(0编辑  收藏  举报