如何看待 tzc 补他一个月前做的题目的题解
首先根据 SG 定理先手必输当且仅当 ( ext{SG}(1)= ext{SG}(2))。考虑从反面入手,拿总情况数减去 ( ext{SG}(1)= ext{SG}(2)) 的方案数。
怎么求 ( ext{SG}(1)= ext{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 ext{\}T),那么显然不能存在 (T) 内部的边,同时由于 (T') 中所有点的 SG 值均非零,所有 (forall xin T'),至少保留了一条 (x) 到 (T) 中节点的边,而 (T') 内部及 (T o T') 的边随便连,乘法原理算一下即可。
- (1,2) SG 值都非零。那么我们还是枚举 SG 值不为 (0) 的子集 (T),令 (T'=S ext{\}T),那么还是不能存在 (T) 内部的边,对于所有 (forall xin 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
",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
",(pw2[m]-dp[(1<<n)-1]+MOD)%MOD);
return 0;
}