「NOI2018」冒泡排序
「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」冒泡排序的更多相关文章
- LOJ2719 「NOI2018」冒泡排序
「NOI2018」冒泡排序 题目描述 最近,小S 对冒泡排序产生了浓厚的兴趣.为了问题简单,小 S 只研究对 1 到n 的排列的冒泡排序. 下面是对冒泡排序的算法描述. 输入:一个长度为n 的排列p[ ...
- Loj #2719. 「NOI2018」冒泡排序
Loj #2719. 「NOI2018」冒泡排序 题目描述 最近,小 S 对冒泡排序产生了浓厚的兴趣.为了问题简单,小 S 只研究对 *\(1\) 到 \(n\) 的排列*的冒泡排序. 下面是对冒泡排 ...
- LOJ #2719. 「NOI2018」冒泡排序(组合数 + 树状数组)
题意 给你一个长为 \(n\) 的排列 \(p\) ,问你有多少个等长的排列满足 字典序比 \(p\) 大 : 它进行冒泡排序所需要交换的次数可以取到下界,也就是令第 \(i\) 个数为 \(a_i\ ...
- loj 2719 「NOI2018」冒泡排序 - 组合数学
题目传送门 传送门 题目大意 (相信大家都知道) 显然要考虑一个排列$p$合法的充要条件. 考虑这样一个构造$p$的过程.设排列$p^{-1}_{i}$满足$p_{p^{-1}_i} = i$. 初始 ...
- LOJ2719. 「NOI2018」冒泡排序 [组合计数]
LOJ 思路 这题我看着题解还搞了几个小时?我也不知道自己在干啥-- 首先你要通过出色的分析能力得到一个结论:一个排列合法当且仅当它的最长下降子序列长度不超过2. 证明?懒得写了. 然后我们不管字典序 ...
- LOJ 2719 「NOI2018」冒泡排序——模型转化
题目:https://loj.ac/problem/2719 首先要发现合法的充要条件是 | LDS | <=2 ! 因为有没用的步数,说明一个元素先往左移.又往右移(不会先往右移再往左移,因为 ...
- 「NOI2018」屠龙勇士(EXCRT)
「NOI2018」屠龙勇士(EXCRT) 终于把传说中 \(NOI2018D2\) 的签到题写掉了... 开始我还没读懂题目...而且这题细节巨麻烦...(可能对我而言) 首先我们要转换一下,每次的 ...
- LOJ #2721. 「NOI2018」屠龙勇士(set + exgcd)
题意 LOJ #2721. 「NOI2018」屠龙勇士 题解 首先假设每条龙都可以打死,每次拿到的剑攻击力为 \(ATK\) . 这个需要支持每次插入一个数,查找比一个 \(\le\) 数最大的数(或 ...
- 「NOI2018」你的名字
「NOI2018」你的名字 题目描述 小A 被选为了\(ION2018\) 的出题人,他精心准备了一道质量十分高的题目,且已经 把除了题目命名以外的工作都做好了. 由于\(ION\) 已经举办了很多届 ...
随机推荐
- vue中的---MVVM(面试必问)
M---Model (数据) V---View (视图) VM---VIewModel (转换器) VIewModel主要做两件事: 1.把 Model 中的数据绑定到View(视图层). 2.监 ...
- springboot 登录实现源代码解析
springboot 可以使用springsecurity 作为安全框架,我们可以使用springsecurity 实现安全登录. springsecurity 是使用一系列的过滤器来实现安全的. 实 ...
- Mysql数据库之备份还原(mysqldump,LVM快照,select备份,xtrabackup)
备份类型: 热备份:读写不受影响 温备份:仅可执行读备份 冷备份:离线备份,读写均不能执行,关机备份 物理备份和逻辑备份 物理备份:复制数据文件,速度快. 逻辑备份:将数据导出之文本文件中,必要时候, ...
- Resin开放远程调试端口
Resin开放远程调试端口在启动加载的resin.xml中,找到 <server-default>, 在其下加入 <jvm-arg>-Xdebug</jvm-arg&g ...
- 洛谷P1192-台阶问题(线性递推 扩展斐波那契)
占坑 先贴上AC代码 回头来补坑 #include <iostream> using namespace std; int n, k; const int mod = 100003; lo ...
- JDK1.8 LocalDate 使用方式;LocalDate 封装Util,LocalDate工具类(一)
未完待续 ........ 由于SimpleDateFormat存在线程安全问题,所以在JDK1.8中使用LocalDate和LocalDateTime来进行日期的工具类使用,下边就是原创的Local ...
- Linux磁盘管理——分区和文件系统
1.分区类型 (1)主分区:最多只能有四个(主分区加扩展分区一共有四个). (2)扩展分区:最多有一个,是主分区中的其中一个.不能存储数据也不能格式化,必须再分成逻辑分区才能使用. (3)逻辑分区:是 ...
- django中models字段的联合限制
一.字段联合使用的限制设置: 有这么个需求,现有两个字段,A字段是选择用户是不是vip, B字段是统计用户的vip等级; 要求: 用户不是vip时,A选择false,B字段为空; 是vip时,A字段选 ...
- django -- 模版语言之过滤器Filters和for循环
前戏 在前面写的图书管理系统中,我们对模版语言应该已经不陌生了,使用{{ }}包裹起来的就是模版语言,只需要记住两种就可以了 {{ 变量名 }} 变量相关的 {% %} ...
- 一步一步编写AVL树
第一步:定义结构体 typedef struct Node{ int d; //data ; //height struct Node* l=NULL; struct Node* r=NULL; No ...