【BZOJ5416】【NOI2018】冒泡排序(动态规划)

题面

BZOJ

洛谷

UOJ

题解

考场推出了就是两个上升子序列,并且最长下降子序列长度不超过\(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\)的后缀和。

那么有:

\[\begin{aligned}
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】冒泡排序(动态规划)的更多相关文章

  1. BZOJ5416 NOI2018冒泡排序(动态规划+组合数学)

    打表可以发现相当于不存在长度>=3的递减子序列. 考虑枚举在哪一位第一次不卡限制.注意到该位一定会作为前缀最大值.判掉已确定位不合法的情况后,现在的问题即为求长度为i.首位>j的合法排列个 ...

  2. BZOJ_5416_[Noi2018]冒泡排序_DP+组合数+树状数组

    BZOJ_5416_[Noi2018]冒泡排序_DP+组合数+树状数组 Description www.lydsy.com/JudgeOnline/upload/noi2018day1.pdf 好题. ...

  3. 【洛谷4769】[NOI2018] 冒泡排序(动态规划_组合数学)

    题目: 洛谷 4769 博客页面左下角的嘴嘴瓜封神之战中的题目 分析: 一个排列交换次数为 \(\frac{1}{2}\sum_{i=1}^{n}|i-p_i|\) 的充要条件是这个排列不存在长度为 ...

  4. [NOI2018]冒泡排序

    https://www.zybuluo.com/ysner/note/1261482 题面 戳我 \(8pts\ n\leq9\) \(44pts\ n\leq18\) \(ex12pts\ q_i= ...

  5. 【UOJ#394】[NOI2018] 冒泡排序

    题目链接 题意 求有多少个字典序严格大于给定排列 \(q_i\) 的排列满足其逆序对数(冒泡排序需要交换的次数)达到下限 \(\frac{1}{2}\sum_{i=1}^n |i-p_i|\) Sol ...

  6. luogu P4769 [NOI2018]冒泡排序 结论 树状数组 卡特兰数

    LINK:冒泡排序 神题. 可以想到爆搜 期望得分5~10分. 打成这个样子心态不得爆炸? 仔细分析 一个不合法序列还有什么标志. 容易想到某个数字离自己位置相反的方向多走了一步. 考虑单独对每个数字 ...

  7. P4769 [NOI2018]冒泡排序(dp)

    传送门 日常膜拜shadowice巨巨的题解 //minamoto #include<bits/stdc++.h> #define R register #define ll long l ...

  8. NOI2010~NOI2018选做

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

  9. Codeforces Round #449 Div. 1

    B:注意到nc/2<=m,于是以c/2为界决定数放在左边还是右边,保证序列满足性质的前提下替换掉一个数使得其更靠近边界即可. #include<iostream> #include& ...

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

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

随机推荐

  1. CodeForces Round #548 Div2

    http://codeforces.com/contest/1139 A. Even Substrings You are given a string s=s1s2…sns=s1s2…sn of l ...

  2. Python中Celery 的基本用法以及Django 结合 Celery 的使用和实时监控进程

    celery是什么 1 celery是一个简单,灵活且可靠的,处理大量消息的分布式系统 2 专注于实时处理的异步任务队列 3 同时也支持任务调度 执行流程 Celery 基本使用 tasks.py i ...

  3. java 工具

    JClassLib 4.2 发布了,该版本支持 Java 7 和 Java 8 的类文件属性查看. JClassLib不但是一个字节码阅读器而且还包含一个类库允许开发者读取,修改,写入Java Cla ...

  4. asp.net core前后端分离

    陆陆续续的看了两个礼拜的前端知识,把vue+vue-router+axios的知识撸了一遍,本来想加个element-ui来实现一下前后端分离,实施的时候却遇到了很多的坑.我本身不在一个软件开发公司上 ...

  5. Azure系列2.1.13 —— CloudBlockBlob

    (小弟自学Azure,文中有不正确之处,请路过各位大神指正.) 网上azure的资料较少,尤其是API,全是英文的,中文资料更是少之又少.这次由于公司项目需要使用Azure,所以对Azure的一些学习 ...

  6. CLOUD计算产品成本嵌套

    1.产品入库单入库的半成品A (无单价) 2.其他出库单上(共耗的)出库的半成品A(无单价) 不管在同车间还是不同车间内都是认定为嵌套的,所以可以计算2遍成本,第1遍不考虑嵌套,第2遍就能计算进去了.

  7. 工作效率提升之Eclipse篇(1):干掉烦人的xml文件的validation

    每次启动maven项目,都会有一堆烦人的xml文件的validation,一旦网络较慢,项目重新启动的时候,这些多余的验证纯属浪费时间. Eclipse上取消validation的方法: 1.菜单[W ...

  8. 关于手机端适配的问题(rem,页面缩放)

    关于手机端适配的问题(rem,页面缩放) 96 进击的小前端 关注 2018.02.02 13:57 字数 320 阅读 19评论 0喜欢 0 相信很多和会和我碰到一样的情况,就是你用rem去写移动端 ...

  9. linux redis服务安装

    redis下载 官网地址:https://redis.io/download 在Linux下安装Redis非常简单,具体步骤如下(官网有说明): 1.下载源码,解压缩后编译源码. $ wget htt ...

  10. python之路--第一类对象,函数名,变量名

    一 . 第一类对象 函数对象可以像变量一样进行赋值 , 还可以作为列表的元素进行使用 可以作为返回值返回 , 可以作为参数进行传递 def func(): def people(): print('金 ...