[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('\n');}
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\n",ans);}return 0;
}}
signed main(){Miracle::main();return 0;
}/*Author: *Miracle*
*/

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

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

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

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

转载于:https://www.cnblogs.com/Miracevin/p/11032215.html

[NOI2018]冒泡排序相关推荐

  1. NOI2018 冒泡排序规律证明

    其实网上对于找到规律之后的部分已经讲的很详细了,在这里只较为严谨的证明一遍这个规律(毕竟网上不少人都说"打表可得"). 首先,我们考虑,对于排列中的一个数i,它对于逆序对的贡献的下 ...

  2. NOI2010~NOI2018选做

    [NOI2010] [NOI2010]海拔 高度只需要0/1,所以一个合法方案就是一个割,平面图求最小割. [NOI2010]航空管制 反序拓扑排序,每次取出第一类限制最大的放置,这样做答案不会更劣. ...

  3. 退役前的最后的做题记录upd:2019.04.04

    考试考到自闭,每天被吊打. 还有几天可能就要AFO了呢... Luogu3602:Koishi Loves Segments 从左向右,每次删除右端点最大的即可. [HEOI2014]南园满地堆轻絮 ...

  4. 首师大附中集训第十一天:OI炼金术

    正题 今天给我们上课的老师很有趣. 交给我们不只是一些题,还有一些题的入手方法,如何把题面转化为题解才是最重要的. OI选手经常遇到的痛点 看了题解之后:啊,好简单,我咋没想到呢? 这题我想出来了,但 ...

  5. 「NOI2018」冒泡排序

    Description 给定1~n的排列p,求所有长度为n的字典序严格大于p的排列中有多少个能被拆分成不超过两个上升子序列.(其实原题是,求有多少个排列进行冒泡排序后交换次数恰为一个下界12∑1≤i≤ ...

  6. NOI2018 游记

    day-2 飞向长沙 上午收拾了收拾东西,下载了动画<爱吃拉面的小泉同学>的前五集. 吃过午饭,就准备坐车去运城机场.高铁飞速,转眼间就到了.我没坐过几次飞机,而且比较恐飞,就很难受qwq ...

  7. PHP算法题:如何实现冒泡排序

    算法原理: 1.比较相邻的元素,如果第一个比第二个大,那么就交换这两个元素. 2.对每一对相邻元素做同样的工作,从第一对开始到最后一对结束,最后的元素应该会是最大的数. 3.除了最后一个元素外,针对其 ...

  8. C#基础——数组(冒泡排序)

    数组 所谓数组,就是相同数据类型的元素按一定的顺序的集合,就是把有限个类型相同的变量用一个名字来命名,然后用编号区分他们的变量的集合,这个名字称为数组名,编号称为下标.组成数组的各个变量称为数组的分量 ...

  9. python实现冒泡排序完整算法_Python实现冒泡排序算法的完整实例

    冒泡排序:顾名思义就是(较小的值)像泡泡一样往上冒,(大的值)往下沉. 实现原理:依次将相邻两个数值进行比较,较小的数值移到左边,较大的数值移到右边,依次比较完第一轮后,最大的数值应该排在最右边.然后 ...

最新文章

  1. 创新方法系列 如何找联系 符号化就是找数学中的等于==关系,遇到等号请留意
  2. 基于物化视图优化_CVPR2017|基于构造多视图子空间中的潜在表示解决聚类问题
  3. python pandas 处理相同标题的csv文件_Python使用pandas处理CSV文件的实例讲解
  4. python 中 * 的使用和表示含义
  5. 数据结构上机实践第二周项目2- 程序的多文件组织
  6. iOS 内购详解及遇到的坑
  7. keras保存的h5、hdf5模型,加载时出现“AttributeError: ‘str‘ object has no attribute ‘decode‘ “
  8. 使用Jorm简单的增删查改数据库
  9. 银行理财子公司的“超级”玩法
  10. 用python怎样解偏微分方程组_用Python数值求解偏微分方程
  11. 基于ROS的仿人机器人运动规划与实现
  12. Hbase跨集群数据同步验证
  13. 疯狂模渲大师链接永久是最新版|怎么安装客户端并激活素材库联系作者加载自营专属素材扩展包高效使用超一流辅助插件脚本工具的步骤教程?...
  14. LZX 定义 - 转帖
  15. Android开发之最新Android Studio推送代码到最新GitHub教程 | Android Studio绑定GitHub | AS令牌登录GitHub | 创建GitHub令牌
  16. 机器学习基石-05-3-Effective Number of Hypotheses
  17. 计算机语言词汇量,汉语作为第二语言的词汇量测量工具研究
  18. 十问旷视印奇、唐文斌:AI企业都在经历「死亡之谷」
  19. python 指定证书验证_使用Python验证SSL证书
  20. 小学计算机老师师德师风演讲稿,小学教师师德师风的演讲稿(精选5篇)

热门文章

  1. 一款实用的前端截图工具
  2. max 宏定义取消:error C2589: error C2059: 语法错误 : “::”
  3. RGB_D_开发征程(使用Kinect)
  4. 【转】Python-面向对象进阶
  5. POJ 1861 Network
  6. form、document.all[].value的数字处理
  7. GPL与LGPL的区别
  8. 运维老鸟职场生活交友经验谈
  9. [zz]NoSQL对比:Cassandra vs MongoDB vs CouchDB vs Redis vs Riak vs HBase vs Membase vs Neo4j
  10. MetaWeblog API中文说明