题解:CF2084E Blossom
简要题意
给定一个长度为
题解
经过一些思考,会发现钦定一个区间的 mex 等于
接下来考虑:做到大于等于一个 mex 值要填的数字个数是固定的。例如要做到
定义
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define SZ(v) ((int)v.size())
#define fs first
#define sc second
#define all(x) (x.begin()),(x.end())
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
const ll mod=1e9+7;
ll fac[5010],inv[5010];
ll qp(ll a,ll b,ll mod=mod){
ll ans=1;
while(b){
if(b&1) ans=ans*a%mod;
a=a*a%mod;b>>=1;
}return ans;
}
ll getInv(ll x,ll p=mod){
return qp(x,p-2,p);
}
ll getC(ll n,ll m,ll p=mod){
return fac[n]*inv[m]%p*inv[n-m]%p;
}
// 动机:做到大于等于一个mex值要填的数字个数是固定的。
// 例如要做到mex>=5,[1,2,4] 出现过且 [0,3] 未出现过,则一定是找两个 -1 填成 [0,3]、而且这个子区间必须包含 [1,2,4] 三个数字
// s[i] : 包含了当前的 [l,r] 作为子区间、且恰有 i 个 -1 的子区间个数
ll T,n,a[5010],pos[5010],pre[5010],s[5010];
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
fac[0]=inv[0]=1;
rep(i,1,5005){
fac[i]=fac[i-1]*i%mod;
inv[i]=getInv(fac[i]);
}
cin>>T;
while(T--){
cin>>n;
memset(pos,0,sizeof(pos));
memset(s,0,sizeof(s));
rep(i,1,n){
cin>>a[i];
pos[a[i]]=i;
pre[i]=pre[i-1]+(a[i]==-1);
}
rep(i,1,n){
rep(j,i,n){
s[pre[j]-pre[i-1]]++;
}
}
int l,r;
l=r=-1; // [l,r] 是包含了 [1,mex-1] 中已确定位置的所有数的最小区间,因此要做到 mex 的区间一定要包含 [l,r]
ll ans=0,c=0; // c 表示 <mex 的有多少个全数组都未出现
rep(mex,1,n+1){
if(pos[mex-1]){
if(l==-1){ // 特判第一次加入出现过的数,将跨过他的区间贡献都减掉
rep(i,1,pos[mex-1]-1){
rep(j,i,pos[mex-1]-1){
s[pre[j]-pre[i-1]]--;
}
}
rep(i,pos[mex-1]+1,n){
rep(j,i,n){
s[pre[j]-pre[i-1]]--;
}
}
l=r=pos[mex-1];
}
else{
if(pos[mex-1]<l){
rep(i,pos[mex-1]+1,l){
rep(j,r,n){
s[pre[j]-pre[i-1]]--;
}
}
l=pos[mex-1];
}
else if(pos[mex-1]>r){
rep(i,1,l){
rep(j,r,pos[mex-1]-1){
s[pre[j]-pre[i-1]]--;
}
}
r=pos[mex-1];
}
}
}
else{
c++;
}
rep(k,c,pre[n]){
ans=(ans+s[k]*getC(k,c)%mod*fac[c]%mod*fac[pre[n]-c])%mod;
}
}
cout<<ans<<"\n";
}
return 0;
}