[bzoj5416]冒泡排序

结论:一个序列是好序列当且仅当其不存在长度为3的下降子序列
证明:考虑提示,一个长度为3的下降子序列必然会交换三次,
而这三次带来的收益实际上只有2,因此不合法
同时还可以得到:第i个数,要么是前缀最大值,要么是之前的mex
(即要么让他之前没有比他大的,要么让他之后没有比他小的)
用f[i][j]表示剩余i个数,当前最大值为n-j的填写方案(先不考虑字典序)
根据上述结论,得到递推式:$f[i][j]=\sum_{k=0}^{j}f[i-1][k]$,其中f[0][0]=1
即$f[i][j]=f[i-1][j]+f[i][j-1]$,解得$f[i][j]=c(i+j,j)-c(i+j,j-2)$(构造的方法:发现任意个c(i+j+k1,j+k2)都符合,再把常数配出来即可)
先预处理阶乘及逆元,然后就可以快速计算f了,之后枚举i,并求出:1.i之前与p相同;2.i位置上比p大的好序列个数
设i之前的最大值是x,mex是y,分别考虑填新最大值和k的贡献,有$ans+=f[n-i][n-x]\cdot [p[i]<y\le x]+f[n-i+1][n-\max(x,p[i])-1]$,累计即可
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mod 998244353
 4 #define N 1200005
 5 int t,n,k,x,y,ans,vis[N],fac[N],inv[N];
 6 int c(int n,int m){
 7     if (m<0)return 0;
 8     return 1LL*fac[n]*inv[m]%mod*inv[n-m]%mod;
 9 }
10 int f(int n,int m){
11     return (c(n+m,m)-c(n+m,m-1)+mod)%mod;
12 }
13 int main(){
14     fac[0]=inv[0]=inv[1]=1;
15     for(int i=1;i<N-4;i++)fac[i]=1LL*fac[i-1]*i%mod;
16     for(int i=2;i<N-4;i++)inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;
17     for(int i=2;i<N-4;i++)inv[i]=1LL*inv[i-1]*inv[i]%mod;
18     scanf("%d",&t);
19     while (t--){
20         scanf("%d",&n);
21         x=ans=0;
22         y=1;
23         for(int i=1;i<=n+1;i++)vis[i]=0;
24         for(int i=1;i<=n;i++){
25             scanf("%d",&k);
26             if ((k<y)&&(y<=x))ans=(ans+f(n-i,n-x))%mod;
27             x=max(x,k);
28             if (x<n)ans=(ans+f(n-i+1,n-x-1))%mod;
29             if ((k!=x)&&(k!=y)){
30                 for(i++;i<=n;i++)scanf("%*d");
31                 break;
32             }
33             vis[k]=1;
34             while (vis[y])y++;
35         }
36         printf("%d\n",ans);
37     }
38 }
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/11506375.html