【学习笔记】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; }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17529988.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(11)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示