CF1837E Play Fixing 题解

首先来考虑什么情况方案数为 \(0\)

  1. 可以确定,在某一层中,两个原本都能晋级的队伍比赛;
  2. 可以确定,在某一层中,两个原本都不能晋级的队伍比赛。

发现假如写出每一场比赛及其胜者,可以形成一棵树形结构,在里面打标记即可判断是否为 \(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;
}
posted @ 2024-04-13 08:56  长安一片月_22  阅读(1)  评论(0编辑  收藏  举报