【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]南园满地堆轻絮 ...
随机推荐
- C#格式化字符串大全
C#格式化字符串大全 分类: VS/C# 1.格式化货币(跟系统的环境有关,中文系统默认格式化人民币,英文系统格式化美元) string.Format("{0:C}" ...
- MySQL导出数据,并转存到Excel表格中
从数据库中导出数据的方法,这里就不提了,网上有很多方法,如果闲麻烦,可以看一下这个:mysql导出数据 其实使用最简单的下面这个语句: mysql > select * from demo in ...
- 【学习总结】Git学习-参考廖雪峰老师教程七-标签管理
学习总结之Git学习-总 目录: 一.Git简介 二.安装Git 三.创建版本库 四.时光机穿梭 五.远程仓库 六.分支管理 七.标签管理 八.使用GitHub 九.使用码云 十.自定义Git 期末总 ...
- PHP导出CSV文件出现乱码的解决方法
在做项目时碰到使用外语的情况下,我们就会使用UTF-8编码.但是,在用PHP导出CSV文件时,如果写入的数据是使用UTF-8编码的日语.韩语之类的外文,就会出现乱码. 要解决PHP生成CSV文件的乱码 ...
- python文件封装成*.exe文件(单文件和多文件)
环境:win10 64位 python3.7 单*.py文件打包Python GUI:程序打包为exe 一.安装Pyinstaller,命令pip install Pyinstaller,(大写的P ...
- MySQL根据某个字段查询重复的数据
select count(*) '个数',mobile '手机号',`name` '用户名' from users group by mobile having(count(*) > 1); = ...
- AJAX返回值问题
ajax同步方式获取返回值,必须以同步请求的的方式获取. //主函数部分 function confirm(id,...)//省略部分参数 { //...省略部分代码 //任务涉及专业 var Maj ...
- [转帖]pfSense软路由系统的使用
图解pfSense软路由系统的使用(NAT功能) http://seanlook.com/2015/04/23/pfsense-usage/ 发表于 2015-04-23 | 更新于: 2015- ...
- python爬虫之scrapy的pipeline的使用
scrapy的pipeline是一个非常重要的模块,主要作用是将return的items写入到数据库.文件等持久化模块,下面我们就简单的了解一下pipelines的用法. 案例一: items池 cl ...
- PHP关联查询
article文章表: aid title content uid 1 文章1 文章1正文内容... 1 2 文章2 文章2正文内容... 1 3 文章3 文章3正文内容... 2 4 文章4 文章4 ...