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

    [NOI2018]冒泡排序


    神题orz

    题目里的这个限制实际上是不存在长度为3的下降子序列

    先看没有字典序要求怎么做。

    首先(O(n^2))递推式(我太弱了这都不会...)

    不存在长度为3的下降子序列那么序列可以分成两个上升序列。

    (f[i][j])表示(i)个数有(j)个大于填过的最大值。

    由于可以分成两个上升序列,所以小于填过的最大值的这些数一定会从小到大填(凑进一个上升序列)大于的可以随便填

    那么转移方程是(f[i][j]=sum_{kleq j}f[i-1][k])

    (f[i-1][j])转移过来就是填一个小于填过的最大值的,否则是填(1)个大于当前最大值的,在所有满足大于当前最大值的数中(从小到大)排名为(j-k)

    另外还有条件(ileq j),边界情况(f[0][0]=1)

    转移方程是个前缀和,可以改写成(f[i][j]=f[i-1][j]+f[i][j-1])

    那么就是组合数学的那一套理论,从((0,0))只走上,右走到((i,j))不触碰线(y=x+1)的方案数,显然有(f[i][j]=C_{i+j-1}^{i-1}-C_{i+j-1}^{i+1}),答案是(f[n][n])

    那么有字典序要求怎么做,p表示字典序下界,q表示求出的排列。枚举(i),字典序要求满足(forall 1leq j<i,p_j=q_j;p_i<q_i)。设(maxp=max_{j=1}^ip_i)而且已经检查过前(i-1)(p)合理。分类讨论:

    1. (q_i<maxp),可以发现是不行的,因为前(i-1)(p)要合理,从刚才的dp知道填小于当前最大值的数就会填没填过得最小数。(p_i)显然没有填过,这个没填过得小数上界就是(p_i)
    2. (q_i>maxp),那么要统计之后的方案数,可以发现是(f[n-i][n-q_i])。总和还是前缀和,(f[n-i+1][n-maxp-1])

    然后要检查新加的(p)是否合理,如果新(p)不是最大值就检查(p)是否是没填过的数中的最小值。乱搞一下就行了。

    #include<bits/stdc++.h>
    #define il inline
    #define vd void
    #define mod 998244353
    typedef long long ll;
    il ll gi(){
        ll x=0,f=1;
        char ch=getchar();
        while(!isdigit(ch))f^=ch=='-',ch=getchar();
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return f?x:-x;
    }
    il int pow(int x,int y){
        int ret=1;
        while(y){
            if(y&1)ret=1ll*ret*x%mod;
            x=1ll*x*x%mod;y>>=1;
        }
        return ret;
    }
    int n,p[600010],t[600010];
    int fact[1200010],ifact[1200010];
    il int C(int n,int m){return 1ll*fact[n]*ifact[m]%mod*ifact[n-m]%mod;}
    il int F(int i,int j){return (C(i+j-1,i-1)-C(i+j-1,i+1)+mod)%mod;}
    int main(){
        int T=gi(),nn=1200000;
        fact[0]=1;for(int i=1;i<=nn;++i)fact[i]=1ll*fact[i-1]*i%mod;
        ifact[nn]=pow(fact[nn],mod-2);for(int i=nn-1;~i;--i)ifact[i]=1ll*ifact[i+1]*(i+1)%mod;
        while(T--){
            n=gi();
            for(int i=1;i<=n;++i)p[i]=gi();
            int maxp=0,ans=0;
            for(int i=1,s=0;i<=n;++i){
                maxp=std::max(p[i],maxp);
                if(maxp==n)break;
                ans=(ans+F(n-i+1,n-maxp-1))%mod;
                if(p[i]!=maxp&&s+1!=p[i])break;
                t[p[i]]=1;while(t[s+1])++s;
            }
            printf("%d
    ",ans);
            for(int i=1;i<=n;++i)t[i]=0;
        }
        return 0;
    }
    
  • 相关阅读:
    JSTL和EL
    SpringMVC 上传文件and过滤器
    SQLServer 大数据查询2
    SQLServer 大数据查询分析
    Oracle分页查询
    Oracle大数据常见优化查询
    Window 下面利用Oid 获取SNMP主机信息 以及计算方法
    window 下面安装net-snmp 简单网关协议
    显示一行省略文字的详细信息
    手机自动隐藏浏览器地址栏
  • 原文地址:https://www.cnblogs.com/xzz_233/p/10993623.html
Copyright © 2011-2022 走看看