zoukankan      html  css  js  c++  java
  • BZOJ_5416_[Noi2018]冒泡排序_DP+组合数+树状数组

    BZOJ_5416_[Noi2018]冒泡排序_DP+组合数+树状数组

    Description

    www.lydsy.com/JudgeOnline/upload/noi2018day1.pdf


    好题。

    合法的排列的交换次数刚好是交换次数的下界,也就是说不能有多余的交换。

    也就是对于ai这个数,只能从i到ai这一个方向走。

    考虑x,y,z三个数(x>y>z),y需要和x、z各交换一次,这显然不能使y这个数满足只向一个方向移动这个条件。

    于是转化为排列的连续下降子序列最多为2。

    考虑DP,设f[i][j]表示最后i个数没填,这i个数中有j个数是大于前缀(1~n-i)最大值的方案数。

    考虑倒数第i个数可以填什么,f[i][j]<---f[i-1][k],有k<=j。

    当k==j时说明填入了一个小于前缀最大值的数,这样的数只可以填最小的一个,有一种方案。

    当k<j时0~j-1中的每个k都对应一个比前缀最大值大的数,每个都有一种方案。

    有DP式子f[i][j]+=f[i-1][k](0<=k<=j),即f[i][j]=f[i-1][j]+f[i][j-1]。

    然后考虑对已知的这个排列怎么做。

    假设前缀最大值为mx,现在处理到的这一位排列上的值是p。

    如果p>mx,这一位可以从p+1开始取值。

    否则,p+1到mx的这些数不能选,如果选了会和前面的mx和后面的p形成3元组。

    故这一位考虑max(mx,p)+1开始的数,比较好的一件事就是这些数一定都没出现,那么对答案的贡献就是$sumlimits_{j=0}^{n-max(mx,p)-1}f[n-i][j]$。

    这玩意又等于f[n-i+1][n-max(mx,p)-1]。

    还要注意每步如果p<mx,求一下后面还有没有小于p的没填的数,如果有就直接停止,这步用个树状数组搞定。

    我们现在的时间复杂度到了$O(n^2)$,发现复杂度瓶颈在求f那里。

    考虑dp方程f[i][j]=f[i-1][j]+f[i][j-1],相当于把所有(x,y)(x<y)的格子抠掉,从(0,0)走到(i,j)的方案数。

    这玩意就是用推卡特兰数的方式翻折计数可得f[i][j]=C(i+j,i)-C(i+j,i+1)。

    然后就做完啦。

    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define N 600050
    #define mod 998244353
    #define _max(x,y) ((x)>(y)?(x):(y))
    typedef long long ll;
    inline char nc() {
        static char buf[100000],*p1,*p2;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    }
    int rd() {
        int x=0; char s=nc();
        while(s<'0'||s>'9') s=nc();
        while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+s-'0',s=nc();
        return x;
    }
    int fac[N<<1],inv[N<<1],n,p[N],c[N];
    int qp(int x,int y) {int re=1;for(;y;y>>=1,x=ll(x)*x%mod) if(y&1) re=ll(re)*x%mod;return re;}
    void fix(int x,int v) {for(;x<=n;x+=x&(-x)) c[x]+=v;}
    int inq(int x) {int re=0;for(;x;x-=x&(-x)) re+=c[x]; return re;}
    void init() {
        int i;
        for(fac[0]=1,i=1;i<=1200000;i++) fac[i]=ll(fac[i-1])*i%mod;
        inv[1200000]=qp(fac[1200000],mod-2);
        for(i=1199999;i>=0;i--) inv[i]=ll(inv[i+1])*(i+1)%mod;
    }
    int C(int n,int m) {
        if(n<m) return 0;
        return ll(fac[n])*inv[m]%mod*inv[n-m]%mod;
    }
    int F(int i,int j) {
        return (C(i+j,i)-C(i+j,i+1)+mod)%mod;
    }
    void solve() {
        memset(c,0,sizeof(c));
        n=rd();
        int i;
        int ans=0,mx=0;
        for(i=1;i<=n;i++) p[i]=rd(),fix(i,1);
        for(i=1;i<n;i++) {
            int flg=inq(p[i]-1),lim=_max(mx,p[i])+1,cnt=n-lim+1;
            ans=(ans+F(n-i+1,cnt-1))%mod;
            if(p[i]<mx&&flg) break;
            mx=_max(mx,p[i]);
            fix(p[i],-1);
        }
        printf("%d
    ",ans);
    }
    int main() {
        init();
        int T;
        T=rd();
        while(T--) solve();
    }
    
  • 相关阅读:
    【训练】9.13 训练赛
    【训练】9.12 训练赛
    【题录】CF#666 Div.2
    【题解】JSOI2009球队收益 / 球队预算
    【申明】——暂别博客园——
    【题解】CF#896 D-Nephren Runs a Cinema
    【题解】洛谷P4707重返现世
    [HNOI2012][BZOJ2732] 射箭 [二分+半平面交]
    平面几何-学习笔记
    [NOI.AC省选模拟赛3.31] 星辰大海 [半平面交]
  • 原文地址:https://www.cnblogs.com/suika/p/9426363.html
Copyright © 2011-2022 走看看