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)−(2ii−1) 的推导方法来得到。
这个请自行搜索,懒得画图了。
代码
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; } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!