Loading web-font TeX/Math/Italic


UOJ#394. 【NOI2018】冒泡排序

原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ394.html

题解

首先我们发现一个数不能既被往左换又被往右换。也就是说不能有任何一个数左边有比他大的,又被有比他小的。

也就是最长下降子序列长度不超过 2 。

所以我们一定可以找到 2 个上升序列包含所有的数。

于是容易想到 O(n2) 的 dp:

dpi,j 表示加入了 i 个数,最大值为 j 的情况下,填完的方案数。

那么,如果下一个数小于 i ,那肯定是填小于 j 的第一个,否则就是填一个比 j 更大的值。

我们把这个东西看做括号序列,填一个比 j 大的值就相当于加入若干个左括号,然后再加入一个右括号;填一个比 j 小的数字就相当于加入一个右括号。

这个东西的方案数可以用类似于卡特兰数的公式 Ci=(2ii)(2ii1) 的推导方法来得到。

这个请自行搜索,懒得画图了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#pragma GCC optimize("Ofast","inline")
#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define For(i,a,b) for (int i=a;i<=b;i++)
#define Fod(i,b,a) for (int i=b;i>=a;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define _SEED_ ('C'+'L'+'Y'+'A'+'K'+'I'+'O'+'I')
#define outval(x) printf(#x" = %d\n",x)
#define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
#define outtag(x) puts("----------"#x"----------")
#define outarr(a,L,R) printf(#a"[%d...%d] = ",L,R);\
                        For(_v2,L,R)printf("%d ",a[_v2]);puts("");
using namespace std;
typedef long long LL;
LL read(){
    LL x=0,f=0;
    char ch=getchar();
    while (!isdigit(ch))
        f|=ch=='-',ch=getchar();
    while (isdigit(ch))
        x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}
const int N=1200005,mod=998244353;
void Add(int &x,int y){
    if ((x+=y)>=mod)
        x-=mod;
}
void Del(int &x,int y){
    if ((x-=y)<0)
        x+=mod;
}
int Pow(int x,int y){
    int ans=1;
    for (;y;y>>=1,x=(LL)x*x%mod)
        if (y&1)
            ans=(LL)ans*x%mod;
    return ans;
}
int n;
int Fac[N],Inv[N];
void prework(){
    int n=N-1;
    for (int i=Fac[0]=1;i<=n;i++)
        Fac[i]=(LL)Fac[i-1]*i%mod;
    Inv[n]=Pow(Fac[n],mod-2);
    Fod(i,n,1)
        Inv[i-1]=(LL)Inv[i]*i%mod;
}
int C(int n,int m){
    if (m>n||m<0)
        return 0;
    return (LL)Fac[n]*Inv[m]%mod*Inv[n-m]%mod;
}
int p[N];
int q[N],head,tail;
int pos,vis[N];
int calc(int x,int y){
    //from (x,y) to (n,n)
    //without crossing line x=y
    if (x>n||y>n)
        return 0;
    assert(x<=y);
    int _x=y+1,_y=x-1;
    return (C(n-x+n-y,n-x)-C(n-_x+n-_y,n-_x)+mod)%mod;
}
void Main(){
    n=read();
    For(i,1,n)
        p[i]=read();
    int ans=0;
    clr(vis);
    pos=1,head=tail=0;
    int Mx=0;
    int x=0,y=0;
    For(i,1,n){
        if (p[i]>Mx){
            Mx=p[i];
            while (pos<p[i]){
                if (!vis[pos]){
                    q[++tail]=pos;
                    vis[pos]=1;
                    y++;
                }
                pos++;
            }
            Add(ans,calc(x,y+2));
            x++,y++;
        }
        else {
            Add(ans,calc(x,y+1));
            if (head>=tail||q[head+1]!=p[i])
                break;
            head++;
            x++;
        }
        vis[p[i]]=1;
    }
    cout<<ans<<endl;
}
int main(){
    prework();
    int T=read();
    while (T--)
        Main();
    return 0;
}

  

posted @   zzd233  阅读(253)  评论(0编辑  收藏  举报
编辑推荐:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
阅读排行:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!

点击右上角即可分享
微信分享提示