题目传送门

  传送门

题目大意

  (相信大家都知道)

  显然要考虑一个排列$p$合法的充要条件。

  考虑这样一个构造$p$的过程。设排列$p^{-1}_{i}$满足$p_{p^{-1}_i} = i$。

  • 初始令$q = (1, 2, \cdots, n)$。
  • 依次考虑$i = 1, 2, \cdots, n$。
    • 设$x = p_i$,如果$q^{-1}_x > i$,那么交换$q_x, q_{x - 1}$。

  上述算法每次交换的时候会使逆序对增加1。

  考虑给出的下界,假设交换的是$i$和$i + 1$。

  不难用归纳法证明$p_i \leqslant i$。

  那么考虑$ \Delta = (i + 1 - p_i + |p_{i + 1} - i|) - (i - p_i + |p_{i + 1} - i - 1|)$。

  • 如果$p_{i + 1} \geqslant i + 1$,那么有$ \Delta = (i + 1 - p_i + p_{i + 1} - i) - (i - p_i + p_{i + 1} - i - 1) =2$
  • 如果$p_{i + 1} \leqslant i$,那么有$\Delta = (i + 1 - p_i + i - p_{i + 1}) - (i - p_i + i + 1 - p_{i + 1}) = 0$

  每次改变量要么为0,要么为2,如果某一次为0,那么将永远达不到下界。

  因此序列合法当仅当上述算法中,每次交换满足$q_x \geqslant x$。

  上述算法中,未确定的数并且可以向前移动的是一段后缀,并且满足$q_x = x$。

  假如某次将$y$向前移动,那么如果一个$z < y$,并且$z$未确定,那么你不能将$z$向前移动。

  然后考虑一下没有字典序限制怎么做,显然这个问题不会更难。

  设$f_{i, j}$表示考虑到排列的前$i$个数,其中最大值为$j$。

  转移考虑最大值有没有发生改变。

  $(i, j)$是平面上的一个点,考虑把这个问题转化到平面上。

  最大值改变等于可以向上走若干步,不变相当于向右走一步。

  另外还需要满足$i \geqslant j$。

  用折线法可以轻松计算出方案数。

  然后我们来考虑原问题。

  字典序严格大于似乎有点烦?考虑小于等于。(其实是我今天想的时候把题意记错了,写完发现过不了样例)

  仍然考虑枚举一个长度为$i - 1$的前缀,然后计算在$i$脱离限制后的方案数。

  下面只考虑长度为$i - 1$的前缀是合法的情况。

  • 如果$a_{i}$是一个前缀最大值,那么考虑$i - 1$的前缀最大值是$mx$,答案加上从$(i, mx), (i, mx + 1), \cdots, (i, a_i - 1)$开始的方案数。
  • 如果$a_i$不是前缀最大值
    • 如果比不是前缀最大值的最小值还大,那么此时前缀$i$不合法,答案加上从$(i, mx)$开始的方案书。
    • 否则对答案没有贡献。

Code

  1. /**
  2. * loj
  3. * Problem#2719
  4. * Accepted
  5. * Time: 652ms
  6. * Memory: 10236k
  7. */
  8. #include <bits/stdc++.h>
  9. using namespace std;
  10. typedef bool boolean;
  11. #define ll long long
  12.  
  13. void exgcd(int a, int b, int& x, int& y) {
  14. if (!b) {
  15. x = 1, y = 0;
  16. } else {
  17. exgcd(b, a % b, y, x);
  18. y -= (a / b) * x;
  19. }
  20. }
  21.  
  22. int inv(int a, int n) {
  23. int x, y;
  24. exgcd(a, n, x, y);
  25. return (x < 0) ? (x + n) : (x);
  26. }
  27.  
  28. const int Mod = 998244353;
  29.  
  30. template <const int Mod = :: Mod>
  31. class Z {
  32. public:
  33. int v;
  34.  
  35. Z() : v(0) { }
  36. Z(int x) : v(x){ }
  37. Z(ll x) : v(x % Mod) { }
  38.  
  39. friend Z operator + (const Z& a, const Z& b) {
  40. int x;
  41. return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
  42. }
  43. friend Z operator - (const Z& a, const Z& b) {
  44. int x;
  45. return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
  46. }
  47. friend Z operator * (const Z& a, const Z& b) {
  48. return Z(a.v * 1ll * b.v);
  49. }
  50. friend Z operator ~(const Z& a) {
  51. return inv(a.v, Mod);
  52. }
  53. friend Z operator - (const Z& a) {
  54. return Z(0) - a;
  55. }
  56. Z& operator += (Z b) {
  57. return *this = *this + b;
  58. }
  59. Z& operator -= (Z b) {
  60. return *this = *this - b;
  61. }
  62. Z& operator *= (Z b) {
  63. return *this = *this * b;
  64. }
  65. friend boolean operator == (const Z& a, const Z& b) {
  66. return a.v == b.v;
  67. }
  68. };
  69.  
  70. Z<> qpow(Z<> a, int p) {
  71. Z<> rt = Z<>(1), pa = a;
  72. for ( ; p; p >>= 1, pa = pa * pa) {
  73. if (p & 1) {
  74. rt = rt * pa;
  75. }
  76. }
  77. return rt;
  78. }
  79.  
  80. typedef Z<> Zi;
  81.  
  82. typedef class Input {
  83. protected:
  84. const static int limit = 65536;
  85. FILE* file;
  86.  
  87. int ss, st;
  88. char buf[limit];
  89. public:
  90.  
  91. Input():file(NULL) { };
  92. Input(FILE* file):file(file) { }
  93.  
  94. void open(FILE *file) {
  95. this->file = file;
  96. }
  97.  
  98. void open(const char* filename) {
  99. file = fopen(filename, "r");
  100. }
  101.  
  102. char pick() {
  103. if (ss == st)
  104. st = fread(buf, 1, limit, file), ss = 0;//, cerr << "str: " << buf << "ed " << st << endl;
  105. return buf[ss++];
  106. }
  107. } Input;
  108.  
  109. #define digit(_x) ((_x) >= '0' && (_x) <= '9')
  110.  
  111. Input& operator >> (Input& in, unsigned& u) {
  112. char x;
  113. while (~(x = in.pick()) && !digit(x));
  114. for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
  115. return in;
  116. }
  117.  
  118. Input& operator >> (Input& in, unsigned long long& u) {
  119. char x;
  120. while (~(x = in.pick()) && !digit(x));
  121. for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
  122. return in;
  123. }
  124.  
  125. Input& operator >> (Input& in, int& u) {
  126. char x;
  127. while (~(x = in.pick()) && !digit(x) && x != '-');
  128. int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
  129. for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
  130. u *= aflag;
  131. return in;
  132. }
  133.  
  134. Input& operator >> (Input& in, long long& u) {
  135. char x;
  136. while (~(x = in.pick()) && !digit(x) && x != '-');
  137. int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
  138. for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
  139. u *= aflag;
  140. return in;
  141. }
  142.  
  143. Input in (stdin);
  144.  
  145. const int N = 6e5 + 5;
  146. const int N2 = N << 1;
  147.  
  148. int T, n;
  149. Zi fac[N2], _fac[N2];
  150.  
  151. void init_fac(int l, int r) {
  152. for (int i = l; i <= r; i++) {
  153. fac[i] = fac[i - 1] * i;
  154. }
  155. _fac[r] = ~fac[r];
  156. for (int i = r; i > l; i--) {
  157. _fac[i - 1] = _fac[i] * i;
  158. }
  159. }
  160. void init_fac(int n) {
  161. static int old = 0;
  162. fac[0] = 1, _fac[0] = 1;
  163. if (n > old) {
  164. init_fac(old + 1, n);
  165. old = n;
  166. }
  167. }
  168. Zi comb(int n, int m) {
  169. return (n < m) ? (0) : (fac[n] * _fac[m] * _fac[n - m]);
  170. }
  171.  
  172. Zi C(int x, int y) {
  173. return comb(x + y, x);
  174. }
  175. Zi S(int x, int y) {
  176. if (y + 1 <= x)
  177. return 0;
  178. return (y == n) ? (1) : (C(n - x, n - y) - C(n + 1 - x, n - 1 - y));
  179. }
  180.  
  181. boolean vis[N];
  182. int main() {
  183. freopen("inverse.in", "r", stdin);
  184. freopen("inverse.out", "w", stdout);
  185. in >> T;
  186. while (T--) {
  187. in >> n;
  188. if (!n) {
  189. puts("0");
  190. continue;
  191. }
  192. init_fac(n << 1);
  193. memset(vis, false, n + 2);
  194. int mx = 0, sc = 1, i = 1, a;
  195. Zi ans = 0;
  196. for (i = 1; i < n; i++) {
  197. in >> a;
  198. if (a > mx) {
  199. for (int j = mx; j < a; j++) {
  200. ans += S(i, j);
  201. }
  202. mx = a;
  203. } else {
  204. while (vis[sc]) sc++;
  205. if (sc ^ a) {
  206. ans += S(i, mx);
  207. break;
  208. }
  209. }
  210. vis[a] = true;
  211. }
  212. if (i == n) {
  213. in >> a;
  214. ans += 1;
  215. } else {
  216. while (++i <= n)
  217. in >> a;
  218. }
  219. ans = S(0, 0) - ans;
  220. printf("%d\n", ans.v);
  221. }
  222. return 0;
  223. }

loj 2719 「NOI2018」冒泡排序 - 组合数学的更多相关文章

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

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

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

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

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

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

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

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

  5. loj#2718. 「NOI2018」归程

    题目链接 loj#2718. 「NOI2018」归程 题解 按照高度做克鲁斯卡尔重构树 那么对于询问倍增找到当前点能到达的高度最小可行点,该点的子树就是能到达的联通快,维护子树中到1节点的最短距离 s ...

  6. loj#2721. 「NOI2018」屠龙勇士

    题目链接 loj#2721. 「NOI2018」屠龙勇士 题解 首先可以列出线性方程组 方程组转化为在模p意义下的同余方程 因为不保证pp 互素,考虑扩展中国剩余定理合并 方程组是带系数的,我们要做的 ...

  7. LOJ2719 「NOI2018」冒泡排序

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

  8. 「NOI2018」冒泡排序

    「NOI2018」冒泡排序 考虑冒泡排序中一个位置上的数向左移动的步数 \(Lstep\) 为左边比它大的数的个数,向右移动的步数 \(Rstep\) 为右边比它大的数的个数,如果 \(Lstep,R ...

  9. loj#2720. 「NOI2018」你的名字

    链接大合集: loj uoj luogu bzoj 单纯地纪念一下写的第一份5K代码.../躺尸 因为ZJOI都不会所以只好写NOI的题了... 总之字符串题肯定一上来就拼个大字符串跑后缀数组啦! ( ...

随机推荐

  1. 电商项目搜寻功能(分页,高亮,solr,规格过滤,价格的排序)

    package cn.wangju.core.service; import cn.wangju.core.pojo.item.Item; import cn.wangju.core.util.Con ...

  2. 这个meta标签会让华为mate10 pro自带浏览器无法粘贴手机收到的验证码信息

     前言 最近在项目中遇到一个问题,注册登录界面点击获取验证码,手机收到短信验证码后可以复制成功,但无法粘贴 让人郁闷的是在其它上手机上的(比如小米,苹果)默认浏览器和其它手机浏览器(比如QQ,夸克,搜 ...

  3. rpc和webservice的关系简述

    RPC(Remote Procedure Call,远程过程调用)是一个很大的概念.它是一种通过网络从远程计算机程序上跨语言跨平台的请求服务.RPC能省略部分接口代码的开发,可以跨机器之间访问对象(J ...

  4. 一些 Java 和 Android 的参考资料

    1. .net程序员转战android第三篇---登录模块之静态登录 2. .net程序员转战android第二篇---牛刀小试 3. .net程序员转战android第一篇---环境部署 4. 一些 ...

  5. Kubernetes Secret(机密存储)

    Kubernetes Secret(机密存储) 官方文档:https://kubernetes.io/docs/concepts/configuration/secret/ 加密数据并存放Etcd中, ...

  6. CentOS 6.9安装MySQL 5.6 (使用yum安装)

    CentOS 6.9安装MySQL 5.6 (使用yum安装) 移除CentOS默认的mysql-libs [root@test01 srv]# whereis mysqlmysql: /usr/li ...

  7. git和小乌龟在windows下安装

    一:所需软件 (1):git 下载地址:https://git-scm.com/download (2):TortoiseGit 下载地址:https://tortoisegit.org/downlo ...

  8. 《深入理解Java虚拟机》并发(第12~13章)笔记

    volatile关键字的作用 所有变量的可见性--仅仅是修改后的值的可见性,不保证并发修改时新值和预期一致.即只保证读,不保证写. 禁止指令重排序--修饰的变量,读写不会指令重排.如变量isReady ...

  9. 基于vue+springboot+docker网站搭建【九】负载均衡

    后台mall-admin 负载均衡 1.新启动一个mall-admin docker实例 docker run -p 9002:9001 --name mall-admin-9002 --link m ...

  10. Vue实战狗尾草博客管理系统第二章

    伙伴们出来啦,探讨各问题,关于项目中大量的表单,大家是怎么处理的? 本章主要内容如下:底层布局,路由配置,github仓库推送关联. 关联GitHub仓库 关联建立在github已创建账号的基础上 登 ...