【学习笔记】CF1608F MEX counting

考虑一个非常简单的问题:给定 M E X MEX MEX序列,怎么算方案数。

我尝试从后往前做,然而发现要多记录一维状态表示已经出现的 M E X MEX MEX种类,难以为继。

首先将 1 ∼ i 1\sim i 1i的数分成两类,一类是 < M E X i <MEX_i <MEXi,这些数对后面的结果不会产生影响,一类是 > M E X i >MEX_i >MEXi,称其为好的元素,我们只关心它的数量。进一步的,相同种类的元素可以看成一个,因此只考虑其种类数。

那么转移无非只有几种:

1.1 1.1 1.1 a i + 1 < M E X i a_{i+1}<MEX_i ai+1<MEXi,显然有 M E X i + 1 = M E X i MEX_{i+1}=MEX_i MEXi+1=MEXi,转移系数为 M E X i MEX_i MEXi
1.2 1.2 1.2 a i + 1 > M E X i a_{i+1}>MEX_i ai+1>MEXi,同样有 M E X i + 1 = M E X i MEX_{i+1}=MEX_i MEXi+1=MEXi,这个好的元素可以新开一个种类,或者合并到原有的种类中去
1.3 1.3 1.3 a i + 1 = M E X i a_{i+1}=MEX_i ai+1=MEXi,那么 M E X i + 1 > M E X i MEX_{i+1}>MEX_i MEXi+1>MEXi,注意 a i + 1 a_{i+1} ai+1已经固定了,要将 ( M E X i , M E X i + 1 ) (MEX_i,MEX_{i+1}) (MEXi,MEXi+1)之间填满,记 D = M E X i + 1 − M E X i − 1 D=MEX_{i+1}-MEX_i-1 D=MEXi+1MEXi1 k k k表示好的元素的数目,方案数 A k D A_{k}^{D} AkD

暴力转移复杂度 O ( n 2 k 2 ) O(n^2k^2) O(n2k2)。这道题神奇的地方在于可以进一步优化。这也非常简单,相当于 M E X MEX MEX那一维平移一格,系数只和 k k k那一维有关所以在转移的同时算一下转移系数就做完了。

复杂度 O ( n 2 k ) O(n^2k) O(n2k)

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define inf 0x3f3f3f3f
#define db double
#define cpx complex<db>
using namespace std;
const int mod=998244353;
int n,K,b[2005];
ll f[105][2005],f2[105][2005],g[105][2005],res,fac[2005],inv[2005];
void add(ll &x,ll y){x=(x+y)%mod;}
int check(int x,int y){return abs(b[x]-y)<=K;}
int getval(int x,int y){return b[x]-K+y;}
ll A(int x,int y){if(x<y)return 0;return fac[x]*inv[x-y]%mod;}
ll fpow(ll x,ll y=mod-2){
    ll z(1);
    for(;y;y>>=1){
        if(y&1)z=z*x%mod;
        x=x*x%mod;
    }return z;
}
void init(int n){
    fac[0]=1;for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
    inv[n]=fpow(fac[n]);for(int i=n;i>=1;i--)inv[i-1]=inv[i]*i%mod;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>K,init(n);
    for(int i=1;i<=n;i++)cin>>b[i];
    if(check(1,0))f[0-(b[1]-K)][1]=1;
    if(check(1,1))f[1-(b[1]-K)][0]=1;
    for(int i=1;i<n;i++){
        memset(g,0,sizeof g);
        memset(f2,0,sizeof f2);
        for(int j=0;j<=2*K;j++){
            for(int k=0;k<=i;k++){
                if(f[j][k]){
                    if(check(i+1,getval(i,j))){
                        add(g[getval(i,j)-(b[i+1]-K)][k],f[j][k]*getval(i,j));
                        add(g[getval(i,j)-(b[i+1]-K)][k+1],f[j][k]);
                        add(g[getval(i,j)-(b[i+1]-K)][k],f[j][k]*k);
                    }
                    if(getval(i,j)+1<=b[i+1]+K){
                        int D=max(0,b[i+1]-K-(getval(i,j)+1));
                        if(k>=D){
                            add(f2[getval(i,j)+D+1-(b[i+1]-K)][k-D],f[j][k]*A(k,D));
                        }
                    }
                }
            }
        }
        for(int k=i;k>=0;k--){
            for(int j=0;j<=2*K;j++){
                if(f2[j][k]){
                    add(g[j][k],f2[j][k]);
                    if(k)add(f2[j+1][k-1],f2[j][k]*k);
                }
            }
        }
        memcpy(f,g,sizeof g);
    }
    for(int i=0;i<=2*K;i++){
        for(int j=0;j<=n;j++){
            if(f[i][j]){
                add(res,f[i][j]*A(n-getval(n,i),j));
            }
        }
    }
    cout<<res;
}
posted @ 2023-04-30 22:20  仰望星空的蚂蚁  阅读(27)  评论(0)    收藏  举报  来源