「NOI2018」冒泡排序

考虑冒泡排序中一个位置上的数向左移动的步数 \(Lstep\) 为左边比它大的数的个数,向右移动的步数 \(Rstep\) 为右边比它大的数的个数,如果 \(Lstep,Rstep\) 中有一个不为 \(0\) ,那么显然不会取到下界,因为产生了浪费的步数,题面给的提示在这里非常有用,如果至少有一个为 \(0\) ,那么显然没有产生浪费操作,取到下界,所以一个合法排列的充要条件就是对于所有位置 \(Lstep\times Rstep=0\) ,即该排列的最长下降子序列长度 \(\leq 2\) 。

先不考虑字典序的限制,只考虑求出一个合法的排列,记 \(dp_{i,j}\) 为前 \(i\) 个数,后面数中有 \(j\) 个比前 \(i\) 个数的最大值要小,此时前 \(i\) 位是一个合法排列的方案数,那么考虑这一步如果选一个小于最大值的数,一定要选最小的数,否则就会出现长度 \(>2\) 的最长下降子序列,否则可以随便选,那么 \(dp_{i,j}\) 可以转移到 \(dp_{i+1,k},j-1\leq k\leq n-i-1\) 。考虑加上字典序的限制,相当于对每一次转移到的 \(k\) 做一个下界限制,稍微改一改就得到了一个 \(\mathcal O(n^2)\) 的 80分做法,这么简单的套路去年考的时候居然没想到。

其实每次 \(k\) 的取值是 \(\geq -1\) 的任何数,因为如果 \(k > n -i+1\) 的话,就再也转移不回 \(dp_{n,0}\) 了,对答案没有影响,然后把每次取的 \(k\) 都加 \(1\) ,问题就转化为 \((0,0)\) 到 \((n,n)\) 不能低于 \(y=-1\) 的一个格路计数问题了,此时不加上字典序的限制就是卡特兰数,加上字典序的限制就枚举再哪里超过了字典序的限制,然后的方案数也是可以 \(O(1)\) 算的,类似于卡特兰数的推导。

code

/*program by mangoyang*/
#pragma GCC optimize("Ofast", "inline")
#include <bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int ch = 0, f = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
const int N = 1200005, mod = 998244353;
int a[N], mx[N], mn[N], js[N], lim[N], inv[N], n;
inline void up(int &x, int y){
x = x + y >= mod ? x + y - mod : x + y;
}
inline int Pow(int a, int b){
int ans = 1;
for(; b; b >>= 1, a = 1ll * a * a % mod)
if(b & 1) ans = 1ll * ans * a % mod;
return ans;
}
inline int C(int x, int y){
if(x < y || x < 0 || y < 0) return 0;
return 1ll * js[x] * inv[y] % mod * inv[x-y] % mod;
}
inline int calc(int x, int y){
int res = 0;
up(res, C(n - x + n - y, n - x));
up(res, mod - C(n - x + n - y, n - y - 1));
return res;
}
namespace Bit{
int s[N];
inline void add(int x){
for(int i = x; i <= n; i += i & -i) s[i]++;
}
inline int query(int x){
int res = 0;
for(int i = x; i; i -= i & -i) res += s[i];
return res;
}
}
inline void solve(){
read(n);
for(int i = 1; i <= n; i++) read(a[i]);
mn[n] = a[n];
for(int i = n - 1; i >= 1; i--) mn[i] = min(a[i], mn[i+1]);
mx[1] = a[1];
for(int i = 2; i <= n; i++) mx[i] = max(mx[i-1], a[i]);
for(int i = n; i >= 1; i--)
lim[i] = Bit::query(mx[i]), Bit::add(a[i]);
for(int i = 1; i <= n; i++) lim[i] += i;
int res = 0;
for(int i = 1; i <= n; i++){
if(lim[i] < n) up(res, calc(i - 1, lim[i] + 1));
if(lim[i-1] > lim[i]) break;
if(lim[i-1] == lim[i] && mn[i] < a[i]) break;
}
cout << res << endl;
for(int i = 0; i <= n; i++) Bit::s[i] = 0;
}
int main(){
js[0] = inv[0] = 1;
for(int i = 1; i < N; i++){
js[i] = 1ll * js[i-1] * i % mod;
inv[i] = Pow(js[i], mod - 2);
}
int T; read(T); while(T--) solve();
}

「NOI2018」冒泡排序的更多相关文章

  1. LOJ2719 「NOI2018」冒泡排序

    「NOI2018」冒泡排序 题目描述 最近,小S 对冒泡排序产生了浓厚的兴趣.为了问题简单,小 S 只研究对 1 到n 的排列的冒泡排序. 下面是对冒泡排序的算法描述. 输入:一个长度为n 的排列p[ ...

  2. Loj #2719. 「NOI2018」冒泡排序

    Loj #2719. 「NOI2018」冒泡排序 题目描述 最近,小 S 对冒泡排序产生了浓厚的兴趣.为了问题简单,小 S 只研究对 *\(1\) 到 \(n\) 的排列*的冒泡排序. 下面是对冒泡排 ...

  3. LOJ #2719. 「NOI2018」冒泡排序(组合数 + 树状数组)

    题意 给你一个长为 \(n\) 的排列 \(p\) ,问你有多少个等长的排列满足 字典序比 \(p\) 大 : 它进行冒泡排序所需要交换的次数可以取到下界,也就是令第 \(i\) 个数为 \(a_i\ ...

  4. loj 2719 「NOI2018」冒泡排序 - 组合数学

    题目传送门 传送门 题目大意 (相信大家都知道) 显然要考虑一个排列$p$合法的充要条件. 考虑这样一个构造$p$的过程.设排列$p^{-1}_{i}$满足$p_{p^{-1}_i} = i$. 初始 ...

  5. LOJ2719. 「NOI2018」冒泡排序 [组合计数]

    LOJ 思路 这题我看着题解还搞了几个小时?我也不知道自己在干啥-- 首先你要通过出色的分析能力得到一个结论:一个排列合法当且仅当它的最长下降子序列长度不超过2. 证明?懒得写了. 然后我们不管字典序 ...

  6. LOJ 2719 「NOI2018」冒泡排序——模型转化

    题目:https://loj.ac/problem/2719 首先要发现合法的充要条件是 | LDS | <=2 ! 因为有没用的步数,说明一个元素先往左移.又往右移(不会先往右移再往左移,因为 ...

  7. 「NOI2018」屠龙勇士(EXCRT)

    「NOI2018」屠龙勇士(EXCRT) 终于把传说中 \(NOI2018D2\) 的签到题写掉了... 开始我还没读懂题目...而且这题细节巨麻烦...(可能对我而言) 首先我们要转换一下,每次的 ...

  8. LOJ #2721. 「NOI2018」屠龙勇士(set + exgcd)

    题意 LOJ #2721. 「NOI2018」屠龙勇士 题解 首先假设每条龙都可以打死,每次拿到的剑攻击力为 \(ATK\) . 这个需要支持每次插入一个数,查找比一个 \(\le\) 数最大的数(或 ...

  9. 「NOI2018」你的名字

    「NOI2018」你的名字 题目描述 小A 被选为了\(ION2018\) 的出题人,他精心准备了一道质量十分高的题目,且已经 把除了题目命名以外的工作都做好了. 由于\(ION\) 已经举办了很多届 ...

随机推荐

  1. Qt中QWidget、QDialog和QMainWindow

    QWidget 类是所有用户界面对象的基类.只有一个"页面" QMainWindow 是一个"窗口".含有菜单栏.状态栏.工具栏.停靠窗口.中心窗口 QDial ...

  2. flink batch wordcount

    1.POJO方式 public class WordCountPojo { public static class Word{ private String word; private int fre ...

  3. Vue组件间通信6种方式

    摘要: 总有一款合适的通信方式. 作者:浪里行舟 Fundebug经授权转载,版权归原作者所有. 前言 组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的 ...

  4. 软件架构的演进,了解单体架构,垂直架构,SOA架构和微服务架构的变化历程

    软件架构演进 软件架构的发展经历了从单体结构.垂直架构.SOA架构到微服务架构的过程,博客里写到了这四种架它们的特点以及优缺点分析,个人学习之用,仅供参考! 1.1.1      单体架构 特点: 1 ...

  5. mysql官网下载对应的mysql包

    1.  在百度搜索mysql,点击mysql官网上下载mysql的地址 在url直接输入mysql的下载地址也可以:https://dev.mysql.com/downloads/mysql/ 如图: ...

  6. 使用Git Flow规范!

    Git Flow常用的分支 Production 分支 也就是我们经常使用的Master分支,这个分支最近发布到生产环境的代码,最近发布的Release, 这个分支只能从其他分支合并,不能在这个分支直 ...

  7. [转]【HttpServlet】HttpServletResponse接口 案例:完成文件下载

    创建时间:6.19 & 6.24 1.案例-完成文件下载 1)  什么情况下会文件下载? 浏览器不能解析的文件就下载 *使用a标签直接指向服务器上的资源 2)什么情况下需要在服务端编写文件下载 ...

  8. 团队第五次作业:alpha2成绩汇总

    一.作业题目 团队第五次作业:alpha2发布 二.作业评分标准 博客评分规则(总分100)博客要求 给出开头和团队成员列表(10') 给出发布地址以及安装手册(20') 给出测试报告(40') 给出 ...

  9. django 基础1

    1.web应用 本质是基于socket实现的应用程序 浏览器---------服务器 2.http协议:应用层协议 1.基于TCP协议 2.基于请求响应 3.短连接 4.无状态 请求协议 浏览器--- ...

  10. erlang程序设计--顺序编程

    erlang模块的基本结构 基本结构-module(filename).-export([funname/num]). c(filename). 编译erlang模块  .bean 结尾的文件 编译后 ...