题解:[AGC016F] Games on DAG
发现这个问题是经典的有向图游戏,考虑先手必败时有 \(SG_1\oplus SG_2=0\),因而我们只需计算出 \(SG_1=SG_2\) 的方案数,再用 \(2^m\) 减去它即可得到答案。
我们可以将这个 DAG 按 \(SG\) 值化为若干层,我们也就是要求出使得 \(1\) 和 \(2\) 处于同一层的方案数。对于一个 \(SG\) 值为 \(k\) 的层,连边有如下的情况:
- 这一层内部不能有边。
- 这一层指向 \(SG>k\) 的点的边是可以随便选的。
- 所有 \(SG>k\) 的点都需要有至少一条边连向至少一个 \(SG=k\) 的点。
因而我们可以按层来处理,考虑设 \(f_S\) 表示点集为 \(S\) 时使得 \(SG_1=SG_2\) 的方案数,枚举 \(S\) 的一个子集 \(T\) 表示 \(SG>k\) 的点集,\(S\setminus T\) 表示 \(SG=k\) 的层。接下来若 \(1,2\in T\) 则从 \(f_T\) 转移而来,否则若 \(1,2\in S\setminus T\) 则 \(T\) 内部的连边随便选即可,因为 \(1\) 和 \(2\) 已经处于同一层。分别根据上述三种连边转移即可。
通过预处理 \(g_{i,S}\) 表示 \(i\) 有多少条出边指向 \(S\) 中的点可以做到 \(O(3^nn)\) 的复杂度。
code:
#include<iostream>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,l,r) for(int i=(l);i>=(r);i--)
using namespace std;
const int maxn=20,maxs=(1<<15)+10,mod=1e9+7;
typedef long long ll;
ll pw[maxn],dp[maxs];
int cnt[maxs][maxn],mp[maxn][maxn];
int main(){
int in,im;
cin>>in>>im;
pw[0]=1;
rep(v1,1,im){
int iu,iv;
scanf("%d %d",&iu,&iv);
mp[iu][iv]++;
pw[v1]=pw[v1-1]*2%mod;
}
int ms=(1<<in)-1;
rep(v1,1,ms){
int p;
per(v2,in,1)if(v1&(1<<v2-1)){
p=v2;
break;
}
rep(v2,1,in)cnt[v1][v2]=cnt[v1^(1<<p-1)][v2]+mp[v2][p];
}
rep(S,1,ms)if((S&3)==3){
dp[S]=1;
for(int T=(S-1)&S;T;T=(T-1)&S)if((T&3)==3||(T&3)==0){
if(T&1){
ll fac=1;
rep(v1,1,in)if(S&(1<<v1-1)){
if(T&(1<<v1-1))fac=fac*(pw[cnt[S^T][v1]]-1)%mod;
else fac=fac*pw[cnt[T][v1]]%mod;
}
dp[S]=(dp[S]+fac*dp[T])%mod;
}
else{
ll fac=1;
rep(v1,1,in)if(S&(1<<v1-1)){
if(T&(1<<v1-1))fac=fac*(pw[cnt[S^T][v1]]-1)%mod*pw[cnt[T][v1]]%mod;
else fac=fac*pw[cnt[T][v1]]%mod;
}
dp[S]=(dp[S]+fac)%mod;
}
}
}
cout<<(pw[im]-dp[ms]+mod)%mod<<endl;
return 0;
}