CF1837E Play Fixing 题解
首先来考虑什么情况方案数为 \(0\):
- 可以确定,在某一层中,两个原本都能晋级的队伍比赛;
- 可以确定,在某一层中,两个原本都不能晋级的队伍比赛。
发现假如写出每一场比赛及其胜者,可以形成一棵树形结构,在里面打标记即可判断是否为 \(0\)。
我们设 \(a_i\) 表示第 \(i\) 层新加的队伍中位置没有确定的个数(这里是从根节点为第 \(0\) 层),\(b_i\) 为可以调换前后顺序的比赛个数,\(dp_i\) 表示第 \(i\) 层的情况数,则有:
\[dp_i=dp_{i-1}\times2^{a_i}\times b_i!
\]
时间复杂度 \(O(2^k)\)。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=(1<<19)+5;
const ll p=998244353;
int k,n,t[20][N],a[20],b[20];
ll dp[20],jc[N];
ll qpow(ll x,int y){
ll re=1;
while(y){
if(y&1) re=re*x%p;
x=x*x%p;y>>=1;
}return re;
}int check(int x,int y,int z){
if(!x){
t[x][y]=z;
return 1;
}if(t[x][y]) return 0;
t[x][y]=z;
if(z>(1<<(x-1))&&t[x][(y+1)/2*4-1-y]&&!t[x-1][(y+1)/2]) return 0;
if(z>(1<<(x-1))) return 1;
return check(x-1,(y+1)/2,z);
}int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>k;
n=1<<k;
jc[0]=dp[0]=1;
for(ll i=1;i<=n;i++)
jc[i]=jc[i-1]*i%p;
for(int i=1,x;i<=n;i++){
cin>>x;
if(x<0) continue;
int g=check(k,i,x);
if(!g){
cout<<0;
return 0;
}
}for(int i=1;i<=k;i++){
for(int j=1;j<=(1<<i);j++){
int l=t[i][j];
if(l>(1<<(i-1))) b[i]++;
}for(int j=1;j<=(1<<i);j+=2)
if(t[i][j]||t[i][j+1]) a[i]++;
a[i]=(1<<(i-1))-a[i];
b[i]=(1<<(i-1))-b[i];
}for(int i=1;i<=k;i++)
dp[i]=dp[i-1]*qpow(2,a[i])%p*jc[b[i]]%p;
cout<<dp[k];
return 0;
}