zoukankan      html  css  js  c++  java
  • [NOI2018]冒泡排序

    [NOI2018]冒泡排序 

    题解

    性质+模型转化

     

    首先,一个排列是“好”的,当且仅当:每个数,要么是前缀最大值,要么是后缀最小值。(讨论i和Pi的关系即可证明)

    也就是,排列不能存在>=3的下降子序列!

    换句话说,假设之前填了i个数,最大值是mx,那么第i+1个数,要么是剩下数的最小值,要么是比mx大的数。

    字典序,肯定按位考虑,转化成没有限制的情况,

    所以先处理没有限制的情况

    这样DP,

    $f[i][j]$剩下i个数,比之前最大值大的数有j个的方案数。

    第n-i+1个位置,要么填最小值,要么填这j个数之一。

    填这j个中第k大的数(它就成为了新的最大值),就只能剩下j-k个比最大值大的了。

    转移:$f[i][j]=sum_{k=0}^{j}f[i-1][k]$,k=0代表填了最小值。前缀和优化

    当然,i>=j必须保证

     

    然后可以卡位。

    之前比最大值大的数有nw个,第i个位置数是ai,

    第i个位置:

    1.$p_i>a_i$

    $p_i$一定是一个比最大值大的数

    如果$a_i$是前缀最大值,则nw=n-a[i]

    否则,nw=n-前缀最大值

    显然为了严格大于,如果nw=0,一定不行。

    自由之后,方案数是:$sum_{j=0}^{nw-1}f[n-i][j]=f[n-i+1][nw-1]$

    2.$p_i=a_i$

    判断$a_i$是不是前缀最大值或者后缀最小值即可

     

    O(T*n^2)

    过不去。

    瓶颈在于O(n^2)DP

    这个DP很模式化啊,,,

    $f[i][j]=sum_{k=0}^{j}f[i-1][k]$

    能不能发现组合意义?

    结论:

    $f[i][j]=C(i+j-1,j)-C(i+j-1,j-2)$

    证明:

    显然必须有i>=j

    本质是,(0,0)往(i,j)走,每次要么往右走,要么往右上走,不越过直线(i=j)的方案数。

    可以对偶成:从(1,1)走,每次往右往上走,不能越过直线(i=j-1)的方案数

    蓝色折线和绿色折线一一对应!

    不合法的也一一对应!(请自行画图)

    从(1,1)走,每次往右往上走,不能越过直线(i=j-1)的方案数

    这个直接卡特兰数一样,对称容斥下即可。

    O(n)

     

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define fi first
    #define se second
    #define mk(a,b) make_pair(a,b)
    #define numb (ch^'0')
    #define pb push_back
    #define solid const auto &
    #define enter cout<<endl
    #define pii pair<int,int>
    using namespace std;
    typedef long long ll;
    template<class T>il void rd(T &x){
        char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);}
    template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
    template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
    template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('
    ');}
    namespace Modulo{
    const int mod=998244353;
    il int ad(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    il int sub(int x,int y){return ad(x,mod-y);}
    il int mul(int x,int y){return (ll)x*y%mod;}
    il void inc(int &x,int y){x=ad(x,y);}
    il void inc2(int &x,int y){x=mul(x,y);}
    il int qm(int x,int y=mod-2){int ret=1;while(y){if(y&1) ret=mul(x,ret);x=mul(x,x);y>>=1;}return ret;}
    template<class ...Args>il int ad(const int a,const int b,const Args &...args) {return ad(ad(a,b),args...);}
    template<class ...Args>il int mul(const int a,const int b,const Args &...args) {return mul(mul(a,b),args...);}
    }
    using namespace Modulo;
    namespace Miracle{
    const int N=1200000+5;
    int n;
    int jie[N],inv[N];
    int C(int n,int m){
        if(n<0||m<0||n<m) return 0;
        return mul(jie[n],inv[m],inv[n-m]);
    }
    int F(int n,int m){
        if(m==0) return 1;
        if(m==1) return n;
        return sub(C(n+m-1,m),C(n+m-1,m-2));
    }
    int a[N];
    bool is[N];
    int main(){
        int t;rd(t);
        n=N-3;
        jie[0]=1;
        for(reg i=1;i<=n;++i) jie[i]=mul(jie[i-1],i);
        inv[n]=qm(jie[n]);
        for(reg i=n-1;i>=0;--i) inv[i]=mul(inv[i+1],i+1);
    
        while(t--){
            rd(n);
            for(reg i=1;i<=n;++i) rd(a[i]);
            int mi=n+3;
            for(reg i=n;i>=1;--i){
                is[i]=0;
                if(a[i]<mi){
                    mi=a[i];
                    is[i]=1;
                }
            }
            int ans=0;
            int nw=n,mx=0;
            for(reg i=1;i<=n;++i){
                nw=n-mx;
                int lp=nw;
                if(a[i]>mx) lp=n-a[i]; 
                if(lp){
                    ans=ad(ans,F(n-i+1,lp-1));
                }
                if(a[i]>mx){
                    mx=a[i];
                }else if(!is[i]){
                    break;
                }
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
    */

    发现本质:一个数要么是前缀最大值,要么是后缀最小值

    无限制?可以DP,f[i][j]记录剩下的数以及比最大值大的数

    字典序?按位考虑。变成无限制。

    O(n^2)?观察DP式子找到组合意义!

     

     

     

  • 相关阅读:
    Django数据库 相关之select_related/prefetch_related
    Django 序列化
    Django 信号
    Django缓存配置和使用
    Django FBV/CBV、中间件、GIT使用
    学员管理系统(SQLAlchemy 实现)
    Oracle的三种高可用集群方案
    linux系统安装硬盘分区建议
    Linux下expdp自动备份
    impdp导入报错ORA-39070:无法打开日志文件
  • 原文地址:https://www.cnblogs.com/Miracevin/p/11032215.html
Copyright © 2011-2022 走看看