【学习笔记】CF1608F MEX counting
考虑一个非常简单的问题:给定 M E X MEX MEX序列,怎么算方案数。
我尝试从后往前做,然而发现要多记录一维状态表示已经出现的 M E X MEX MEX种类,难以为继。
首先将 1 ∼ i 1\sim i 1∼i的数分成两类,一类是 < 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+1−MEXi−1,
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;
}

浙公网安备 33010602011771号