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

news2024/12/23 1:45:49

[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 do
	for j = 1 to n - 1 do
		if(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.coloradmin.cn/o/1409502.html

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

相关文章

光环云与跨境智算云网实验室联合发布“数据全链路安全与合规解决方案”

1月19日&#xff0c;国际数据经济产业创新大会在上海临港新片区召开&#xff0c;光环云受邀出席。会上&#xff0c;光环云与“上海国际数据港创新实验室——跨境智算云网实验室”联合发布“数据全链路安全与合规解决方案”&#xff0c;助力企业数据跨境流动更加便捷、安全、高效…

数据结构之二叉树的遍历

数据结构是程序设计的重要基础&#xff0c;它所讨论的内容和技术对从事软件项目的开发有重要作用。学习数据结构要达到的目标是学会从问题出发&#xff0c;分析和研究计算机加工的数据的特性&#xff0c;以便为应用所涉及的数据选择适当的逻辑结构、存储结构及其相应的操作方法…

BPM、低代码和人工智能:实现灵活、创新与转型的关键结合

随着零售业格局的不断演变&#xff0c;零售商正被迫在一个日益活跃、竞争日益激烈的客户驱动型市场中展开竞争。随着互联网上产品信息和评论的出现&#xff0c;消费者的态度发生了巨大的变化——购物者不再依赖销售人员来获取信息。他们现在知道的和许多零售销售人员一样多&…

解决 BeanUtil.copyProperties 不同属性直接的复制

1、引入hutool <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.16</version> </dependency> hutool官网 2、直接上例子 对象&#xff1a;User.java Data public class User {p…

一文了解Ceph原理以及常见ceph指令

一、Ceph介绍 什么是分布式存储&#xff1f; 与集中式存储相反&#xff0c;分布式存储通常采用存储单元集群的形式。并且具有在集群节点之间进行数据同步和协调的机制。其目的是为了通过服务器解决大规模&#xff0c;高并发情况下的Web访问问题。 Ceph是一个统一的、分布式的存…

2017年认证杯SPSSPRO杯数学建模D题(第二阶段)教室的合理设计全过程文档及程序

2017年认证杯SPSSPRO杯数学建模 D题 教室的合理设计 原题再现&#xff1a; 某培训机构租用了一块如图&#xff08;见附件&#xff09;所示的场地&#xff0c;由于该机构开设了多种门类的课程&#xff0c;所以需要将这块场地通过加入一些隔墙来分割为多个独立的教室和活动区。…

数据目录驱动测试——深入探讨Pytest插件 pytest-datadir

在软件测试中,有效管理测试数据对于编写全面的测试用例至关重要。Pytest插件 pytest-datadir 提供了一种优雅的解决方案,使得数据目录驱动测试变得更加简单而灵活。本文将深入介绍 pytest-datadir 插件的基本用法和实际案例,助你更好地组织和利用测试数据。 什么是pytest-da…

分布式锁实现(mysql,以及redis)以及分布式的概念(续)redsync包使用

道生一&#xff0c;一生二&#xff0c;二生三&#xff0c;三生万物 这张尽量结合上一章进行使用&#xff1a;上一章 这章主要是讲如何通过redis实现分布式锁的 redis实现 这里我用redis去实现&#xff1a; 技术&#xff1a;golang&#xff0c;redis&#xff0c;数据结构 …

github 推送报错 ssh: connect to host github.com port 22: Connection timed out 解决

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

未来已来:概念车展漫游可视化的震撼之旅

随着科技的飞速发展&#xff0c;汽车行业正经历着前所未有的变革。而在这场变革中&#xff0c;概念车展无疑是一个引领潮流、展望未来的重要舞台。 想象一下&#xff0c;你站在一个巨大的展厅中&#xff0c;四周陈列着各式各样的概念车。它们有的造型独特&#xff0c;有的功能先…

[BJDCTF2020]ZJCTF,不过如此(特详解)

php特性 1.先看代码&#xff0c;提示了next.php&#xff0c;绕过题目的要求去回显next.php 2.可以看到要求存在text内容而且text内容强等于后面的字符串&#xff0c;而且先通过这个if才能执行下面的file参数。 3.看到用的是file_get_contents()函数打开text。想到用data://协…

真心话大冒险!关于自动驾驶的现状和未来,Mobileye的回答是?

过去的十年&#xff0c;可以说是从主动安全、辅助驾驶到自动驾驶快速演进的周期。这其中&#xff0c;无论是技术迭代&#xff0c;还是成本优化&#xff0c;以及技术和商业化路线的争论&#xff0c;备受行业关注。 同时&#xff0c;市场上的声音&#xff0c;也很多。有激进、谨慎…

详细Nginx和PHP-FPM的进程间通信使用

工作中考虑到PHP-FPM效率&#xff0c;发现PHP-FPM和NGINX的进程通信不止配置端口这一种方式:bowtie: Nginx和PHP-FPM的进程间通信有两种方式,一种是TCP,一种是UNIX Domain Socket. 其中TCP是IP加端口,可以跨服务器.而UNIX Domain Socket不经过网络,只能用于Nginx跟PHP-FPM都在同…

基于YOLOv8的摔倒行为检测系统(Python源码+Pyqt6界面+数据集)

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文主要内容:通过实战基于YOLOv8的摔倒行为检测算法&#xff0c;从数据集制作到模型训练&#xff0c;最后设计成为检测UI界面 人体行为分析AI算法&#xff0c;是一种利用人工智能技术对人体行为进行检测、跟踪和分析的方法。通过计算…

linux基础指令【中篇】

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 补充上篇的细节1.sta…

C#~Winform代码调整快捷键

右移 选中要移动的代码 -> Tab 结果&#xff1a; 左移 选中要移动的代码 -> ShiftTab 结果&#xff1a; 自动调整 选中需要调整的代码 -> CtrlKD 调整后&#xff1a;

论文解读--Wideband 120 GHz to 140 GHz MIMO Radar:System Design and Imaging Results

120~140GHz宽带MIMO雷达&#xff1a;系统设计和成像结果 摘要 本文提出了一种工作频率在120 GHz ~ 140 GHz之间的宽带FMCW MIMO雷达传感器。该传感器基于SiGe技术制造的雷达芯片组&#xff0c;并使用MIMO方法来提高角度分辨率。MIMO操作通过发射机的时域复用实现。该雷达能够通…

[蓝桥杯]真题讲解:冶炼金属(暴力+二分)

蓝桥杯真题视频讲解&#xff1a;冶炼金属&#xff08;暴力做法与二分做法&#xff09; 一、视频讲解二、暴力代码三、正解代码 一、视频讲解 视频讲解 二、暴力代码 //暴力代码 #include<bits/stdc.h> #define endl \n #define deb(x) cout << #x << &qu…

Python 中的多进程(01/2):简介

一、说明 本文简要而简明地介绍了 Python 编程语言中的多处理&#xff08;多进程&#xff09;。解释多处理的基本信息&#xff0c;如什么是多处理&#xff1f;为什么用多处理&#xff1f;在python中怎么办等。 二、什么是多处理&#xff1f; 多处理是指系统同时支持多个处理器的…

【Android】Android中的系统镜像由什么组成?

文章目录 总览Boot Loader 的加锁与解锁Boot 镜像内核RAM diskARM 中的设备树 (Device Tree) /System 和/Data 分区镜像参考 总览 各种Android设备都只能刷专门为相应型号的设备定制的镜像。 厂商会提供一套系统镜像把它作为“出厂默认”的 Android 系统刷在设备上。 一个完…