专栏文章
CF2154F2
CF2154F2题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minabbfv
- 此快照首次捕获于
- 2025/12/01 23:08 3 个月前
- 此快照最后确认于
- 2025/12/01 23:08 3 个月前
考虑合法排列的形态。有序排列是例外。对于其他排列,会有一段红色的前缀和蓝色的后缀不动,中间的一段红色的每个数都必须往后动,蓝色的必须往前动。因此中间这一段都满足 ,且红色的满足 ,蓝色的满足 。此时分成两种情况讨论:
若可能形成有序排列,即不存在 。假设实际排列不是有序排列,枚举 在哪个段里,其他段确定,这一段内元素自由染红或蓝,排除有序排列的情况,为 。每一段加起来,最后加上有序排列的 。
否则, 通过 的元素可以唯一确定不为 的位置的颜色。设红蓝的分界点为 ,红色为 ,蓝色为 。对不有序的排列都可唯一确定分界点为第一个 的数,所以可以枚举每个 。
枚举 后,枚举每个 的连续段 将方案数相乘,我们可以计算出这一段里红蓝的个数。若 颜色相同,方案数是 。否则不妨设 是红色, 是蓝色。 的红色数为 , 的红色数为 , 的红色数为 ,方案数 。发现对于每一段,只有 个 可能合法,直接枚举这些 就是 的。
CPP#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
int t,n,a[1000005],p2[1000005],fac[1000005],vac[1000005],ans[1000005];
bool type[1000005];
int C(int n,int m){
return n<0||m<0||n<m?0:1ll*fac[n]*vac[m]%mod*vac[n-m]%mod;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),p2[0]=fac[0]=vac[0]=vac[1]=1;
for(int i=2;i<=1000000;i++)vac[i]=1ll*vac[mod%i]*(mod-mod/i)%mod;
for(int i=1;i<=1000000;i++)p2[i]=2*p2[i-1]%mod,fac[i]=1ll*fac[i-1]*i%mod,vac[i]=1ll*vac[i]*vac[i-1]%mod;
cin>>t;
while(t--){
cin>>n,a[n+1]=n+1;
for(int i=1;i<=n;i++)cin>>a[i];
bool flag=1;
for(int i=1;i<=n;i++)if(a[i]!=-1&&a[i]!=i)flag=0;
if(flag){
int ans=1;
for(int i=1,pre=0;i<=n+1;i++)if(a[i]!=-1)ans=(ans+p2[i-pre-1]-(i-pre))%mod,pre=i;
cout<<ans<<'\n';
continue;
}
int l=2,r=n,prod=1;
for(int i=1,pre=0;i<=n+1;i++){
if(a[i]==-1)continue;
if(a[i]<i)flag=1,type[i]=0;
else if(a[i]>i)flag=1,type[i]=1;
else type[i]=flag;
if(type[i]==type[pre])prod=1ll*prod*C(i-pre-1,a[i]-a[pre]-1)%mod;
else l=max(l,a[i]-i+a[pre]+1),r=min(r,a[i]-pre+a[pre]);
pre=i;
}
if(!prod||l>r){
cout<<0<<'\n';
continue;
}
for(int i=l;i<=r;i++)ans[i]=1;
for(int i=1,pre=0;i<=n+1;i++){
if(a[i]==-1)continue;
if(type[i]!=type[pre])for(int j=l;j<=r;j++)ans[j]=1ll*ans[j]*C(i-pre-1,i-a[i]+j-a[pre]-1)%mod;
pre=i;
}
int sum=0;
for(int i=l;i<=r;i++)sum=(sum+ans[i])%mod;
cout<<1ll*sum*prod%mod<<'\n';
}
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...