这道题题目的要求是冒泡排序的交换次数达到下界1/2Sigma |i-pi| ,首先想要达到下界,那么每一个元素的贡献也要达到下界,即1/2|i-pi|,所以对于每一个元素,和它有关的逆序对数量都是|i-pi|。
接下来进行第二步的转化,
1.i>pi 逆序对数量为i-pi,可以推出比pi小的数都在i位置前
2.i<pi 逆序对数量为pi-i,可以推出比i位置前的数都是小于pi的
由这两个结论,我们可以得出,我们原问题等价于统计可以被划分成两个上升序列的序列的个数。
证明:考虑一个数pi,第一种情况比它小的数都在它之前,那么包含它的最长下降子序列最长为2。
第二种情况前面的数都小于它,那么包含它的最长下降子序列同样最长为2.
因此合法序列可以被划分为两个上升子序列。
首先我们可以设计出一个n^2的dp ,我们令Fi,j 表示填了前i个数,剩余没填的数里还有j个大于当前的最大值的方案数。
那么我们首先可以填一个大于当前最大值的数,这样是不会破坏原来所说的性质的。
第二种转移我们可以填一个小于当前最大值的数,那样我们只能选择没填的数里最小的一个填上去,才能不破坏性质。
考虑这个dp的组合意义
我们令j'为当前小于最大值的数则j'=n-i-j;
我们考虑每次进行第一种转移,j'增加,加入了最大值增加量个左括号。
每次i++,增加了一个右括号。
答案就是长度2*n合法括号序列的数量。显然是卡特兰数。
也可以转化为从0,0到2*n,0每次往左上或左下走的合法路径条数,不能越过x轴
我们考虑字典序的限制。等于我们提前加了一些左括号。即固定了起点开始走的合法路径条数。
这个我们可以通过将起点关于x=-1作对称进行容斥。反转后的起点到终点的路径必定经过x=-1,即为不合法路径。
附代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 998244353;
inline int read(){int w=1,s=0;char ch=getchar();while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}return w*s;}
ll fac[2000010],Ifac[2000010],n;
int p[1000010];
bool used[1000010];
inline ll ksm(ll x,ll y){ll res=1;while(y){if(y&1) res=res*x%mod;x=x*x%mod;y>>=1;} return res;}
inline ll C(int p,int q){
return fac[p]*Ifac[q]%mod*Ifac[p-q]%mod;
}
inline void Init(){
fac[0]=Ifac[0]=1;
int t=2e6;
for(register int i=1;i<=t;++i) fac[i]=1ll*fac[i-1]*i%mod;
Ifac[t]=ksm(fac[t],mod-2);
for(register int i=t-1;i;--i) Ifac[i]=1ll*Ifac[i+1]*(i+1)%mod;
}
inline ll get(int a,int b)
{
if(b>a) return 0;
int tot=2*a-b;ll res=C(tot,a);
return (res+mod-C(tot,a+1))%mod;
}
int main()
{
int T=read();
Init();
while(T--){
memset(used,0,sizeof(used));
n=read();
for(register int i=1;i<=n;++i) p[i]=read();
int Max=0,pos=0,Min=1;
ll ans=0;
for(register int i=1;i<=n;++i,--pos)
{
if(p[i]>Max) pos+=(p[i]-Max);
Max=max(Max,p[i]);
ans=ans+get(n-i+1,pos+1);ans=ans%mod;
used[p[i]]=1;
for(;used[Min];++Min);
if(Max>p[i]&&p[i]>Min) break;
}
cout<<ans<<'\n';
}
return 0;
}