NOI2018:冒泡排序

[题面](https://www.luogu.org/problemnew/show/P4769)

吐槽:好像发的pdf题面的冒泡是挂挂的,还好我还会冒泡排序(逃

具体思路:首先发现当这个序列的最长下降序列长度大于2时,一定不符合要求

那么我们可以想出一个$O(n^2)$的DP

$f_i,j$表示前$i$个数,设其中最大的数为$mx$,后面的数中有$j$个数比$mx$小的方案数

然后发现$f_i,j$可以转移至$i+1,j+k$的位置($k>=-1$)

那么答案就是从$(0,0)$走到$(n,0)$的方案数,且路径不能低于x轴

那么可以把路径看成一个括号序列,x坐标+1,表示多一个右括号,y坐标表示左括号和右括号的差

那么我们枚举从哪个位置突破字典序的限制,然后后面就可以随机游走,求方案数就组合数搞搞,不能低于x轴就对称搞搞

AC代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e6+4;
int n,i,j,m,a[N],ha=998244353;
int fact[N],inv[N],factinv[N],vis[N];
int qpow(int a,int b)
{
    int ans=1;
    while(b)
    {
        if(b%2)ans=ans*a%ha;
        a=a*a%ha;b=b/2;
    }
    return ans;
}
int T;
int C(int x,int y)
{
    if(x>y)return 0;
    return fact[y]*factinv[x]%ha*factinv[y-x]%ha;
}
int cal(int h,int dis)
{
    if((dis-h)%2)return 0;
    int x=(dis-h)/2+h,y=(dis-h)/2;
    return C(y,dis);
}
int calc(int x,int y)
{
    if(x<y)
    {
        return 0;
    }
    int dis=(x*2-y),h=y;
    return (ha+cal(y,2*x-y)-cal(y+2,2*x-y))%ha;
}
signed main()
{
    fact[0]=1,factinv[0]=1;
    for(i=1;i<=2000000;i++)
    {
        if(i>1)inv[i]=(ha-(ha/i))*inv[ha%i]%ha;else inv[i]=1;
        fact[i]=fact[i-1]*i%ha;
        factinv[i]=factinv[i-1]*inv[i]%ha;
    }
    scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld",&n);
        for(i=1;i<=n;i++)scanf("%lld",&a[i]);
        int mx=0,low=1,sum=0,ans=0;memset(vis,0,sizeof vis);
        for(i=1;i<n;i++)
        {
            if(a[i]>mx)sum+=a[i]-mx,mx=a[i];vis[a[i]]=1;
            for(;vis[low];low++);
            ans=(ans+calc(n-i+1,sum+1))%ha;
            if(mx>a[i]&&a[i]>low)break;sum--;
        }
        printf("%lld
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Orange-User/p/9367216.html