【BZOJ5416】【NOI2018】冒泡排序(动态规划)
【BZOJ5416】【NOI2018】冒泡排序(动态规划)
题面
题解
考场推出了就是两个上升子序列,并且最长下降子序列长度不超过\(2\)。。。然后大力暴力状压\(dp\)混了\(44\)分。。。这个结论并不是很难证明,考虑一下冒泡排序的过程就好了。
实际上\(O(n^2)\)并不是很难吧。。。
先不考虑字典序的问题,设\(f[i][j]\)表示长度为\(i\)的\([1,i]\)的排列,并且第一个数为\(j\)的合法排列方案数。
考虑如何转移,如果\(j=1\),那么后面怎么放都行,所以$\displaystyle f[i][1]=\sum_{j=1}^{i-1}f[i-1][j] $。注意一下后面的数所谓的排列我们理解为抛掉第一个数之后,剩下的位置重新离散后的值。
否则不以\(1\)开头,如果下一个数比\(j\)大,那么是没有问题的,这部分就是\(\displaystyle f[i][j]\rightarrow \sum_{k=j}^{i-1}f[i-1][k]\)。
那么如果下一个数比\(j\)小,如果下个数不是\(1\)的话,显然当前\(j\)、下一个数、\(1\),就构成了一个不合法的状态。
实际上想想就是,所有小于当前数的数,也就是\([1,j-1]\)在排列中一定是相对有序的,即强制顺序排列。
那么这样子的方案数是什么呢?\(f[i-1][j-1]\)。
为啥呢?
如果\(j=2\),显然后面接一个\(1\)是没有任何问题的。
否则的话,我们把接在后面的这样子一个合法的排列给弄出来,然后把\(j\)接在开头,显然会出现\(j,j-1,1\)这个不合法的东西,那么我们做个转换,强制令\(j-1\)变成\(1\),然后剩下的\([1,j-2]\)这些数全部加一,不难发现这样子\([1,j-1]\)这些数就被强制顺序排列了。
这样子我们就有转移了:\(\displaystyle f[i][j]=\begin{cases}\sum_{k=1}^{i-1}f[i-1][k]&j=1\\\sum_{k=j-1}^{i-1}f[i-1][k]&j>1\end{cases}\)
好的,我们显然可以\(O(n^2)\)的算出\(f\)数组,考虑怎么计算答案了。
显然是前面一部分卡紧范围,然后从某个特定的位置开始,大于了给定的字典序,然后后面就可以放飞自我了。
假装我们紧紧卡住了\([1,i-1]\)这些位置,然后从\(i\)位置开始放飞自我。
假设有在\(q[i..n]\)中有\(k\)个数要小于\(q[1..i]\)中的前缀最大值。
这里分情况讨论贡献,并且以下讨论都是在前缀合法的情况下进行的。判断当前是否合法只需要维护前缀最大值、前缀次大值以及后缀最小值,实时检查是否合法即可。
显然后面要填进去的数中,大于当前前缀最大值的数是没有任何影响的,而小于前缀最小值的数则强制从小往大填。怎么强制从小往大填呢?显然任何一个以大于前缀最大值开头的填法中,这一段都被强制按照顺序填,显然开头比前缀最大值要大的时候,一定被强制按照顺序填了,所以这一部分的贡献是\(\displaystyle \sum_{j=k+1}^{n-i+1}f[n-i+1][j]\)。
然而发现似乎并没有以没有以比前缀最大值小的数中的最小值为开头的贡献。
的确没有考虑,但是因为这里要限制字典序,所以当前这里填的数必须比前缀最大值大后面才不会受到字典序的限制,所以这里就没有这一部分贡献了。。。
这样子就可以做到\(O(Tn^2)\)的复杂度。
复杂度的瓶颈在于求解\(f\)数组以及其后缀和。
显然不能在通过求解\(f\)再求解其后缀和了,这样子效率太差。
设\(\displaystyle s[n][m]=\sum_{i=m}^n f[n][i]\),即\(f\)的后缀和。
那么有:
s[n][m]&=\sum_{i=m}^n f[n][i]\\
&=\sum_{i=m}^n \sum_{j=i-1}^{n-1} f[n-1][j]\\
&=\sum_{j=m-1}^{n-1} f[n-1][j]\sum_{i=m}^{j+1} 1\\
&=\sum_{j=m-1}^{n-1} f[n-1][j]+\sum_{j=m-1}^{n-1} f[n-1][j]\sum_{i=m+1}^{j+1} 1\\
&=s[n-1][m-1]+\sum_{i=m+1}^{n}\sum_{j=i-1}^{n-1}f[n-1][j]\\
&=s[n-1][m-1]+s[n][m+1]
\end{aligned}\]
明摆着找组合意义,走\(n\)步,每次可以向上走一步或者向下走一步,最终停在\(m\)位置,且中途不能走到\(0\)下面去的方案数。
行,那不就是这题??
那么推出来就是\(\displaystyle s[n][m]={2n-m\choose n-m}-{2n-m\choose n-m-1}\)
然后???
然后就做完了啊。。
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 1200001
#define MOD 998244353
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int jc[MAX],jv[MAX],inv[MAX];
int n,q[MAX],mn[MAX],ans;
int c[MAX];
int lb(int x){return x&(-x);}
void add(int x,int w){while(x<=n)c[x]+=w,x+=lb(x);}
int getsum(int x){int s=0;while(x)s+=c[x],x-=lb(x);return s;}
int C(int n,int m){if(m>n)return 0;return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
int S(int n,int m){if(m>n)return 0;return (C(n+n-m,n-m)+MOD-C(n+n-m,n-m-1))%MOD;}
int main()
{
jc[0]=jv[0]=inv[0]=inv[1]=1;
for(int i=1;i<MAX;++i)jc[i]=1ll*jc[i-1]*i%MOD;
for(int i=2;i<MAX;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<MAX;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
int T=read();
while(T--)
{
n=read();ans=0;if(!n){puts("0");continue;}
for(int i=1;i<=n;++i)q[i]=read(),c[i]=0;
mn[n]=q[n];for(int i=n-1;i;--i)mn[i]=min(mn[i+1],q[i]);
for(int i=1;i<=n;++i)add(q[i],1);
for(int i=1,mx=0,smx=0;i<=n;++i)
{
if(smx>mn[i])break;
int k=getsum(max(mx,q[i]));add(q[i],-1);
ans=(ans+S(n-i+1,k+1))%MOD;
if(q[i]<mx)smx=max(smx,q[i]);mx=max(mx,q[i]);
}
printf("%d\n",ans);
}
}
【BZOJ5416】【NOI2018】冒泡排序(动态规划)的更多相关文章
- BZOJ5416 NOI2018冒泡排序(动态规划+组合数学)
打表可以发现相当于不存在长度>=3的递减子序列. 考虑枚举在哪一位第一次不卡限制.注意到该位一定会作为前缀最大值.判掉已确定位不合法的情况后,现在的问题即为求长度为i.首位>j的合法排列个 ...
- BZOJ_5416_[Noi2018]冒泡排序_DP+组合数+树状数组
BZOJ_5416_[Noi2018]冒泡排序_DP+组合数+树状数组 Description www.lydsy.com/JudgeOnline/upload/noi2018day1.pdf 好题. ...
- 【洛谷4769】[NOI2018] 冒泡排序(动态规划_组合数学)
题目: 洛谷 4769 博客页面左下角的嘴嘴瓜封神之战中的题目 分析: 一个排列交换次数为 \(\frac{1}{2}\sum_{i=1}^{n}|i-p_i|\) 的充要条件是这个排列不存在长度为 ...
- [NOI2018]冒泡排序
https://www.zybuluo.com/ysner/note/1261482 题面 戳我 \(8pts\ n\leq9\) \(44pts\ n\leq18\) \(ex12pts\ q_i= ...
- 【UOJ#394】[NOI2018] 冒泡排序
题目链接 题意 求有多少个字典序严格大于给定排列 \(q_i\) 的排列满足其逆序对数(冒泡排序需要交换的次数)达到下限 \(\frac{1}{2}\sum_{i=1}^n |i-p_i|\) Sol ...
- luogu P4769 [NOI2018]冒泡排序 结论 树状数组 卡特兰数
LINK:冒泡排序 神题. 可以想到爆搜 期望得分5~10分. 打成这个样子心态不得爆炸? 仔细分析 一个不合法序列还有什么标志. 容易想到某个数字离自己位置相反的方向多走了一步. 考虑单独对每个数字 ...
- P4769 [NOI2018]冒泡排序(dp)
传送门 日常膜拜shadowice巨巨的题解 //minamoto #include<bits/stdc++.h> #define R register #define ll long l ...
- NOI2010~NOI2018选做
[NOI2010] [NOI2010]海拔 高度只需要0/1,所以一个合法方案就是一个割,平面图求最小割. [NOI2010]航空管制 反序拓扑排序,每次取出第一类限制最大的放置,这样做答案不会更劣. ...
- Codeforces Round #449 Div. 1
B:注意到nc/2<=m,于是以c/2为界决定数放在左边还是右边,保证序列满足性质的前提下替换掉一个数使得其更靠近边界即可. #include<iostream> #include& ...
- 退役前的最后的做题记录upd:2019.04.04
考试考到自闭,每天被吊打. 还有几天可能就要AFO了呢... Luogu3602:Koishi Loves Segments 从左向右,每次删除右端点最大的即可. [HEOI2014]南园满地堆轻絮 ...
随机推荐
- 线程中的samaphore信号量及event事件
一.信号量 samaphore: 在程序中意思为同时允许几个线程运行,比如我们去水上乐园的滑梯玩时,有四个滑梯,每一个滑梯上当没有人在中间玩滑下去时才允许上人,四个滑梯1,2,3,4,同时最多四个人, ...
- Python_socket常见的方法、网络编程的安全注意事项、socketsever模块、浏览器中在一段时间记录用户的登录验证机制
1.socket常见的方法 socket_常见方法_服务器端 import socket from socket import SOL_SOCKET,SO_REUSEADDR sk = socket. ...
- 现代程序设计 homework-06
写代码爽还是读代码爽? 当然是写代码爽好吧... 读代码明显是读+写两倍的工作量好么... 本次作业要求: 1) 把程序编译通过, 跑起来. 读懂程序,在你觉得比较难懂的地方加上一些注释,这样大家就能 ...
- js总结:利用js获取下拉框的value值和文本值
select下拉框在项目开发中是经常用到的,特别是在联级菜单方面的应用更为广泛.但是,对于一些初学者来说,如何获取下拉框子节点option的value值和文本内容,还是有一点难度的. html代码: ...
- C# foreach内部原理
我们知道使用foreach的一个要求是对象必须继承自IEnumerable接口 这样才可以进行迭代 那内部是怎么实现的呢 这个时候会将对应的foreach语句转换为一个while循环 并且通过Move ...
- MRP设置自动执行
1.在计划向导中,找到调度运算,如下配置, 2.配置成功后自动生成执行计划
- java & jdk
java & jdk JDK 下载太慢 & java 12 https://download.oracle.com/otn-pub/java/jdk/12.0.1+12/69cfe15 ...
- 莫烦scikit-learn学习自修第一天【scikit-learn安装】
1. 机器学习的分类 (1)有监督学习(包括分类和回归) (2)无监督学习(包括聚类) (3)强化学习 2. 安装 (1)安装python (2)安装numpy >=1.6.1 (3)安装sci ...
- qtp自动化测试-条件语句 if select case
1 if 语句 if condition then end if If condition Then [statements] [ElseIf condition-n Then [else ...
- Spring Boot 构建电商基础秒杀项目 (十) 交易下单
SpringBoot构建电商基础秒杀项目 学习笔记 新建表 create table if not exists order_info ( id varchar(32) not null defaul ...