P4769 [NOI2018] 冒泡排序 洛谷黑题题解附源码

[NOI2018] 冒泡排序

题目背景

请注意,题目中存在 n = 0 n=0 n=0 的数据。

题目描述

最近,小 S 对冒泡排序产生了浓厚的兴趣。为了问题简单,小 S 只研究对 1 1 1 n n n 的排列的冒泡排序。

下面是对冒泡排序的算法描述。

输入:一个长度为 n 的排列 p[1...n]
输出:p 排序后的结果。
for i = 1 to n dofor j = 1 to n - 1 doif(p[j] > p[j + 1])交换 p[j] 与 p[j + 1] 的值

冒泡排序的交换次数被定义为交换过程的执行次数。可以证明交换次数的一个下界是 1 2 ∑ i = 1 n ∣ i − p i ∣ \frac 1 2 \sum_{i=1}^n \lvert i - p_i \rvert 21i=1nipi,其中 p i p_i pi 是排列 p p p 中第 i i i 个位置的数字。如果你对证明感兴趣,可以看提示。

小 S 开始专注于研究长度为 n n n 的排列中,满足交换次数 = 1 2 ∑ i = 1 n ∣ i − p i ∣ = \frac 1 2 \sum_{i=1}^n \lvert i - p_i \rvert =21i=1nipi 的排列(在后文中,为了方便,我们把所有这样的排列叫「好」的排列)。他进一步想,这样的排列到底多不多?它们分布的密不密集?

小 S 想要对于一个给定的长度为 n n n 的排列 q q q,计算字典序严格大于 q q q 的“好”的排列个数。但是他不会做,于是求助于你,希望你帮他解决这个问题,考虑到答案可能会很大,因此只需输出答案对 998244353 998244353 998244353 取模的结果。

输入格式

输入第一行包含一个正整数 T T T,表示数据组数。

对于每组数据,第一行有一个正整数 n n n,保证 n ≤ 6 × 1 0 5 n \leq 6 \times 10^5 n6×105

接下来一行会输入 n n n 个正整数,对应于题目描述中的 q i q_i qi,保证输入的是一个 1 1 1 n n n 的排列。

输出格式

输出共 T T T 行,每行一个整数。

对于每组数据,输出一个整数,表示字典序严格大于 q q q 的「好」的排列个数对 998244353 998244353 998244353 取模的结果。

样例 #1

样例输入 #1

1
3
1 3 2

样例输出 #1

3

样例 #2

样例输入 #2

1
4
1 4 2 3

样例输出 #2

9

提示

更多样例

更多样例请在附加文件中下载。

样例 3

见附加文件中的 inverse3.ininverse3.ans

样例 1 解释

字典序比 1 3 2 1 \ 3 \ 2 1 3 2 大的排列中,除了 3 2 1 3 \ 2 \ 1 3 2 1 以外都是「好」的排列,故答案为 3 3 3

数据范围

下面是对本题每个测试点的输入规模的说明。

对于所有数据,均满足 T = 5 T = 5 T=5(样例可能不满足)。

n m a x n_\mathrm{max} nmax 表示每组数据中 n n n 的最大值, ∑ n \sum n n 表示所有数据的 n n n 的和。

测试点 n m a x = n_\mathrm{max} = nmax= ∑ n ≤ \sum n \leq n特殊性质
1 8 8 8 5 n m a x 5 \ n_\mathrm{max} 5 nmax
2 9 9 9 5 n m a x 5 \ n_\mathrm{max} 5 nmax
3 10 10 10 5 n m a x 5 \ n_\mathrm{max} 5 nmax
4 12 12 12 5 n m a x 5 \ n_\mathrm{max} 5 nmax
5 13 13 13 5 n m a x 5 \ n_\mathrm{max} 5 nmax
6 14 14 14 5 n m a x 5 \ n_\mathrm{max} 5 nmax
7 16 16 16 5 n m a x 5 \ n_\mathrm{max} 5 nmax
8 16 16 16 5 n m a x 5 \ n_\mathrm{max} 5 nmax
9 17 17 17 5 n m a x 5 \ n_\mathrm{max} 5 nmax
10 18 18 18 5 n m a x 5 \ n_\mathrm{max} 5 nmax
11 18 18 18 5 n m a x 5 \ n_\mathrm{max} 5 nmax
12 122 122 122 700 700 700 ∀ i q i = i \forall i \enspace q_i = i iqi=i
13 144 144 144 700 700 700
14 166 166 166 700 700 700
15 200 200 200 700 700 700
16 233 233 233 700 700 700
17 777 777 777 4000 4000 4000 ∀ i q i = i \forall i \enspace q_i = i iqi=i
18 888 888 888 4000 4000 4000
19 933 933 933 4000 4000 4000
20 1000 1000 1000 4000 4000 4000
21 266666 266666 266666 2000000 2000000 2000000 ∀ i q i = i \forall i \enspace q_i = i iqi=i
22 333333 333333 333333 2000000 2000000 2000000
23 444444 444444 444444 2000000 2000000 2000000
24 555555 555555 555555 2000000 2000000 2000000
25 600000 600000 600000 2000000 2000000 2000000

提示

下面是对交换次数下界是 1 2 ∑ i = 1 n ∣ i − p i ∣ \frac 1 2 \sum_{i=1}^n \lvert i - p_i \rvert 21i=1nipi 的证明。

排序本质上就是数字的移动,因此排序的交换次数应当可以用数字移动的总距离来描述。对于第 i i i 个位置,假设在初始排列中,这个位置上的数字是 pi,那么我们需要将这个数字移动到第 p i p_i pi 个位置上,移动的距离是 ∣ i − p i ∣ \lvert i - p_i \rvert ipi。从而移动的总距离就是 ∑ i = 1 n ∣ i − p i ∣ \sum_{i=1}^n \lvert i - p_i \rvert i=1nipi,而冒泡排序每次会交换两个相邻的数字,每次交换可以使移动的总距离至多减少 2 2 2。因此 1 2 ∑ i = 1 n ∣ i − p i ∣ \frac 1 2 \sum_{i=1}^n \lvert i - p_i \rvert 21i=1nipi 是冒泡排序的交换次数的下界。

并不是所有的排列都达到了下界,比如在 n = 3 n = 3 n=3 的时候,考虑排列 3 2 1 3 \ 2 \ 1 3 2 1,这个排列进行冒泡排序以后的交换次数是 3 3 3,但是 1 2 ∑ i = 1 n ∣ i − p i ∣ \frac 1 2 \sum_{i=1}^n \lvert i - p_i \rvert 21i=1nipi 只有 2 2 2

感谢给我讲了这个题的神仙跳瓜 jumpmelon\textrm{jumpmelon}jumpmelon

首先看给的提示,我们可以发现在这样的排序方式下,对于每一个数都只向目标位置方向走,不换向。那么对于一个数 xxx,如果有一个比它大的数在它前面,那么它必须向左走;如果有一个比它小的数在它后面,那么它必须向右走。这样的排列是不合法的,即要求不存在长度超过 222 的下降子序列。

根据 Dilworth\textrm{Dilworth}Dilworth 定理,最长下降子序列的长度不超过 222,即整个排列最多被划分成 222 个上升子序列。

先不考虑字典序严格大于 qqq 的限制。

我们记 fi,jf_{i, j}fi,j 为前 iii 个的最大值为 jjj 后面 n−in - ini 个位置的方案数。我们考虑第 iii 个数填什么。注意 i⩽ji \leqslant jij

如果填比 jjj 大的数,那么一定可以接在 jjj 的后面;如果要填比 jjj 小的数,那么必须填当前还没有填的中最小的,否则上升子序列将不止 222 个,所以

fi,j={fi+1,k     (k>j)fi+1,j     =∑k=jnfi+1,k\begin{aligned} f_{i, j} &= \begin{cases} f_{i + 1, k} \ \ \ \ \ (k > j) \\ f_{i + 1, j} \ \ \ \ \ \end{cases} \\ &= \sum_{k = j}^n f_{i + 1, k} \end{aligned} fi,j={fi+1,k     (k>j)fi+1,j     =k=jnfi+1,k

但是直接这样递推是 O(n2)O(n^2)O(n2) 的。我们把它以图像的形式表示,fi,jf_{i, j}fi,j 即表示从点 (i,j)(i, j)(i,j) 开始,每次向右走一步,向上走 x(x⩽0)x(x \leqslant 0)x(x0) 步,不与直线 y=x−1y = x - 1y=x1 (因为 i⩽ji \leqslant jij) 相交,走到点 (n,n)(n, n)(n,n) 的方案数。

如图,即从点 A(i,j)A(i, j)A(i,j) 走到 B(n,n)B(n, n)B(n,n) 的不与直线 y=x−1y = x - 1y=x1 相交的方案数。

首先,如果不考虑与直线不相交,即为走 n−in - ini 次,每次选择向上走 x(x⩾0)x (x \geqslant 0)x(x0) 步,一共走了 n−jn - jnj 步的方案数。模仿插板法,因为 xxx 可以取 000,我们把总个数加上划分数 n−in - ini,变成 n−i+n−jn - i + n - jni+nj 个物品划分成 n−in - ini 块的方案数,即 (2n−i−j−1n−i−1)\dbinom{2n - i - j - 1}{n-i-1}(ni12nij1)

再模仿 Catalan\textrm{Catalan}Catalan 数的推法,看第一个与直线 y=x−1y = x - 1y=x1 相交的位置。找点 (i,j)(i, j)(i,j) 关于直线 y=x−1y = x - 1y=x1 的对称点 (j+1,i−1)(j + 1, i - 1)(j+1,i1), 由于方案一一对应,所以,从点 (i,j)(i, j)(i,j) 出发,经过直线的方案数即为从点 (j+1,i−1)(j + 1, i - 1)(j+1,i1) 出发到点 (n,n)(n, n)(n,n) 的方案数。

得到

fi,j=calc(i,j)−calc(j+1,i−1)=(2n−i−j−1n−i−1)−(2n−i−j−1n−j−2)\begin{aligned} f_{i, j} &= calc(i, j) - calc(j + 1, i - 1) \\ &= \dbinom{2n - i - j - 1}{n - i - 1} - \dbinom{2n - i - j - 1}{n - j - 2} \\ \end{aligned} fi,j=calc(i,j)calc(j+1,i1)=(ni12nij1)(nj22nij1)

这样就得到 fffO(1)O(1)O(1) 求解啦!(然而 O(n2)O(n^2)O(n2) 有足足 808080 分,真香)

可以发现 f0,0f_{0, 0}f0,0 即为 Catalan\textrm{Catalan}Catalan 数,可以得到 121212 分。

回到有限制字典序严格大于 qqq 的原题上来。考虑一位一位枚举,假设当前枚举到第 iii 项,我们计数证前 i−1i - 1i1 项与 qqq 相同,第 iii 项大于 qiq_iqi 的排列个数。

mx=max⁡j=1i−1qjmx = \max_{j = 1}^{i - 1} q_jmx=maxj=1i1qjmnmnmn 为当前还没有用过的最小的数,v=qiv = q_iv=qi。第 iii 位只能填 mnmnmn 或大于 mxmxmx 的数。分类讨论

  • v=mnv = mnv=mn

    (因为 mnmnmn 是最小可以填的,所以 vvv 的下界是 mnmnmn。)

    此时,第 iii 项不能填 mnmnmn,只能大于 mxmxmx,故后面 n−in - ini 项的填法有 ∑k=mx+1nfi,k\sum_{k = mx + 1}^n f_{i, k}k=mx+1nfi,k 种(kkk 为第 iii 位填的数)。

  • mn<v<mxmn < v < mxmn<v<mx

    此时第 iii 位没有可以填的,后面不再存在合法方案。(但是还是要读完)

  • v⩾mxv \geqslant mxvmx

    此时第 iii 位可以填 mnmnmn 或大于 mxmxmx 的数,方案数为 ∑k=mxnfi,k\sum_{k = mx}^n f_{i, k}k=mxnfi,k

问题又来了,怎么求 fff 的前缀和呢?考虑 fff 的递推式 fi,j=∑k=jnfi+1,kf_{i, j} = \sum_{k = j}^n f_{i + 1, k}fi,j=k=jnfi+1,k,这正是一个前缀和的形式。所以

∑k=limnfi,k=fi−1,lim\sum_{k = lim}^n f_{i, k} = f_{i - 1, lim} k=limnfi,k=fi1,lim

不用像其他题解上说的要用树状数组,复杂度 O(Tn)O(Tn)O(Tn)(还好写)

完结撒花~

代码

注意数组要开 2n2n2n,写起来很简单,但是最开始由于没彻底搞懂想了半天

#include <bits/stdc++.h>
using namespace std;namespace TYC
{typedef long long ll;const int N = 1.2e6, p = 998244353;int fac[N + 5], inv[N + 5], vis[N + 5];inline int read(){int v = 0, fl = 0, ch = getchar();while (!isdigit(ch))fl |= (ch == '-'), ch = getchar();while (isdigit(ch))v = v * 10 + ch - '0', ch = getchar();return fl ? -v : v;}inline int qpow(int v, int tim){int ans = 1;for (; tim; tim >>= 1, v = (ll)v * v % p)if (tim & 1)ans = (ll)ans * v % p;return ans;}void init(){fac[0] = 1;for (int i = 1; i <= N; i++)fac[i] = (ll)fac[i - 1] * i % p;inv[N] = qpow(fac[N], p - 2);for (int i = N; i; i--)inv[i - 1] = (ll)inv[i] * i % p;}inline int C(const int n, const int m){return (n < 0 || m < 0 || n < m) ? 0 : int((ll)fac[n] * inv[m] % p * inv[n - m] % p);}int n;inline int F(const int i, const int j){return i <= j && j <= n ? (C(2 * n - i - j - 1, n - i - 1) - C(2 * n - i - j - 1, n - j - 2) + p) % p : 0;}void work(){init();int T = read();while (T--){n = read();memset(vis, 0, sizeof(int[n + 1]));int ans = 0, mx = 0, mn = 1, flag = 0, v;for (int i = 1; i <= n; i++){v = read();if (flag)continue;ans = (ans + (F(i - 1, max(mx, v) + 1) + p) % p) % p;if (mx > v && v > mn)flag = 1;mx = max(mx, v);vis[v] = 1;while (vis[mn]) mn++;}printf("%d\n", ans);}}
}int main()
{TYC::work();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/649401.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

韦东山嵌入式Liunx入门笔记一

文章目录 一、嵌入式Linux二、Ubuntu系统2-1 安装软件2-2 Linux文件(1) 文件架构(2)文件属性(3)文件命令(4) 解压、压缩文件(5) 网络命令 2-3 vi编辑器2-4 Ubuntu下包管理 三、配置网卡四、安装后续学习使用的软件4-1 MobaXterm4-2 FileZilla4-3 Source Insight4.04-4 下载BSP4…

Open CASCADE学习|圆柱螺旋线绘制原理探究

1、圆柱螺旋线绘制原理 在OCC中&#xff0c;圆柱面的参数方程为&#xff1a; 设P为&#xff08;x0,y0,z0&#xff09;,则 xx0r*cos(u) yy0r*sin(u) zz0v 但u、v之间有关系时&#xff0c;此方程表达为圆柱螺旋线&#xff0c;u、v之间为线性关系时是等螺距螺旋线&#xff0…

文件上传之秒传功能

秒传是一种文件的传输机制&#xff0c;用于在文件已经存在于目标服务器上时&#xff0c;通过校验文件的唯一标识&#xff0c;实现快速而无需从新上传整个文件&#xff0c;它解决了重复上传相同文件的问题&#xff0c;提高了文件传输的效率和节省了带宽资源。 技术阐述&#xff…

免 费 小程序商城搭建之鸿鹄云商 SAAS云产品概述

【SAAS云平台】打造全行业全渠道全场景的SaaS产品&#xff0c;为店铺经营场景提供一体化解决方案&#xff1b;门店经营区域化、网店经营一体化&#xff0c;本地化、全方位、一站式服务&#xff0c;为多门店提供统一运营解决方案&#xff1b;提供丰富多样的营销玩法覆盖所有经营…

软件测试面试八股文(2024新版)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1、你的测试职业发展是什么&#xff1f; 测试经验越多&#xff0c;测试能力越高。所以我的职业发…

【unity实战】实现蓄力丢手榴弹、烟雾弹、燃烧弹的效果

文章目录 爆炸燃烧烟雾效果资产手榴弹丢手雷烟雾弹、燃烧弹实现手雷每次撞墙弹发出音效&#xff08;补充&#xff09;完结 爆炸燃烧烟雾效果资产 https://assetstore.unity.com/packages/vfx/particles/war-fx-5669 手榴弹 手榴弹配置好刚体&#xff0c;碰撞体 新增脚本Th…

Qlik Sense : Store With Retry (保存重试机制)

Background sometime you cannot store the file directly ,maybe there are another process are reading/storeing the file , so you would need to wait another proecess done and retry . then we come up this solution . 有时您不能直接存储文件&#xff0c;可能还有…

实验:eNSP AR通过telnet远程登录另外一台AR

实验2&#xff1a;eNSP AR通过telnet远程登录另外一台AR 基于实验1的基础上来进行&#xff0c;我们通过AR2220登录AR3260 首先设置远程登录密码 1、user-interface vty 0 4 进入用户的虚拟终端 2、设置密码 set authentication password cipher Huawei 这里的意思就是设置密…

数据结构(C语言版)代码实现(四)——静态单链表的部分代码实现

目录 参考材料、格式 头文件SLinkList.h 库、宏定义、函数类型声明 线性表的静态单链表存储结构 按值查找 初始化静态链表 分配空间 回收空间 打印已用链表中的元素 求集合(A-B)U(B-A)中的元素&#xff08;重点介绍&#xff09; 调试过程 修改报错与警告 调试 完整…

找不到msvcp110.dll怎么办,msvcp110.dll丢失修复方法分享

当计算机系统中无法找到msvcp110.dll这个特定的动态链接库文件时&#xff0c;可能会引发一系列运行问题和功能受限的情况。msvcp110.dll是Microsoft Visual C Redistributable Package的一部分&#xff0c;对于许多基于Windows的应用程序来说&#xff0c;它是至关重要的运行组件…

vue模拟聊天页面列表:滚动到底部,滚动到顶部触发加载更多

先看下效果&#xff1a; 代码&#xff1a; <template><div><div style"text-align: center"><button click"scrollTop">滚动到顶部</button><button click"scrollBottom">滚动到底部</button></d…

Vue深入学习2—虚拟DOM和Diff算法

1、snabbdom 是什么&#xff1f; snabbdom是“速度"的意思&#xff0c;源码只有200行&#xff0c;使用TS写的&#xff0c;让东西变得模块化 2、snabbdom 的 h 函数如何工作&#xff1f; h函数用于产生虚拟节点&#xff0c;同时也可以嵌套使用&#xff0c;得到虚拟DOM树&am…

kuberneters可视化界面-kuboard

一、kuboard安装 可以选用&#xff0c;docker和docker-commpose kuberneters 安装 kuboard官网 1、 docker安装 sudo docker run -d \--restartunless-stopped \--namekuboard \-p 80:80/tcp \-p 10081:10081/tcp \-e KUBOARD_ENDPOINT"http://192.168.1.10:80" …

linux的kali安装,换源,更新包

下载kali kali.org进入官网后点第二个 然后点第一个 解压kali 下载后获得.7z压缩包&#xff0c;建议移动到合适自己电脑的位置进行解压&#xff0c;我喜欢放在D盘 启动kali 双击进入解压出的文件夹&#xff0c;将唯一一个.vmx文件用vmware打开&#xff08;没装的自行提前装…

数据结构奇妙旅程之二叉树题型解法总结

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

【深度学习】CodeFormer训练过程,如何训练人脸修复模型CodeFormer

文章目录 BasicSR介绍环境数据阶段 I - VQGAN阶段 II - CodeFormer (w0)阶段 III - CodeFormer (w1) 代码地址&#xff1a;https://github.com/sczhou/CodeFormer/releases/tag/v0.1.0 论文的一些简略介绍&#xff1a; https://qq742971636.blog.csdn.net/article/details/134…

链路追踪-调用链跟踪-Jaeger

文章目录 一、什么是链路跟踪二、OpenCensusOpenCensus 主要特点OpenTracing标准基本概念Span 三、典型服务端产品什么是OpenTracing?opentracing 使用介绍 四、JaegerJaeger 包含的模块Jaeger-client&#xff08;客户端库&#xff09; 五、Jaeger服务容器化部署过程问题整理 …

csdn黑色背景用法

在edge浏览器下&#xff0c;下载油猴脚本管理器 脚本下载 edge扩展 效果图如下&#xff1a;&#xff1a;&#xff1a;

[ACM学习] 进制转换

进制的本质 本质是每一位的数位上的数字乘上这一位的权重 将任意进制转换为十进制 原来还很疑惑为什么从高位开始&#xff0c;原来从高位开始的&#xff0c;可以被滚动地乘很多遍。 将十进制转换为任意进制

适合深夜发朋友圈的心灵鸡汤(整理70句)

1、很多时候&#xff0c;我们赢得了口舌&#xff0c;却失去了感情。 2、失恋到极致的时候&#xff0c;我真的会用后退来保护自己。 3、全身心地去爱&#xff0c;你可能会受到伤害&#xff0c;但这是完整人生的唯一方式。 4、自由不是想干什么就干什么&#xff0c;而是不想干…