Loj #2719. 「NOI2018」冒泡排序

题目描述

最近,小 S 对冒泡排序产生了浓厚的兴趣。为了问题简单,小 S 只研究对 *\(1\) 到 \(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] 的值


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

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

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

输入格式

从文件 inverse.in 读入数据。

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

对于每组数据,第一行有一个正整数 \(n\),保证 \(n \leq 6 \times 10^5\)。

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

输出格式

输出到文件 inverse.out 中。

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

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

数据范围与提示

\(n\leq 600000 ,\sum n\leq 2000000\)

可以发现,如果一个序列合法,那么对于任意一个数,它在冒泡排序的过程中只能最多向一边移动。我们有知道,如果一个数的右边有比它小的数,那么它一定会向右移动,同理如果左边有一个比它大的数,那么也会向左移动。所以一个合法序列要求每个数不能同时在两边都有逆序对。

根据这个原理就很容易得到一个状压的做法了。

然后考虑有没有多项式复杂度的做法。考虑从左往右填数。假设已经填了数中最大的是\(mx\),那么还没有填的数中比\(mx\)小的那一部分已经有了左边的逆序对,就不能有右边的逆序对了,所以这部分数应该保持升序,而比\(mx\)大的那部分则没有这个限制。

于是就可以\(DP\)了。设\(f_{i,j}\)表示还剩\(i\)个数没有填,没有限制的数有\(j\)个的方案数。

初始化:\(f_{0,0}=1\)。

转移:

\[f_{i,j}=f_{i-1,j}+\sum_{k=0}^{j-1}f_{i-1,k}\\
=\sum_{k=0}^jf_{i-1,k}
\]

考虑新加进来的数是否有限制,如果没有,那么知道他是第几个没有限制的数就可以知道剩下的没有限制的数还有多少个了。

这个\(DP\)方程是\(O(n^3)\)的,但是我们也可以将这个方程写成以下形式:

\[\begin{align}
f_{i,j}&=f_{i,j-1}+f_{i-1,j}\\
f_{i,0}&=1
\end{align}
\]

这样就变成\(O(n^2)\)的了。

然后考虑这个\(f_{n,m}\)的组合意义:一个网格上,从\((0,0)\)走到\((n,m)\),每步只能向上或者向右走,并且\(i\geq j\)的方案数。也就是说整个过程不能经过\(y=x+1\)这条直线。

于是就可以用一个叫折线定理的东西求解。求出\((0,0)\)对\(y=x+1\)的对称点\((-1,1)\),然后求出由\((-1,1)\)走到\((n,m)\)的方案数。\((-1,1)\)到\((n,m)\)的每条路径一定经过了\(y=x+1\)且唯一对应原问题的一个非法情况(将路径沿\(y=x+1\)翻折)。所以:

\[f_{n,m}=\binom{n+m}{m}-\binom{n+m}{m-1}
\]

考虑处理字典序的问题。我们枚举\(k\),表示\(k\)之前的所有位置都与\(q_i\)相同,然后在\(k\)这个位置填的数\(>q_k\)。假设\(q_{1\ldots k}\)中最大的是\(q_{mx}\),在\(q_{mx+1\ldots n}\)中比\(q_{mx}\)大的有\(cnt\)个,则此时答案为\(f_{n-i+1,cnt-1}\)

我们分情况讨论一下为什么是这样的。我们设\(k\)之前的最大值为\(mx\)

  1. \(q_k>mx\)。在\(k\)这个位置只能填入比\(q_k\)大的数,显然也比\(mx\)大,所以这些数是没有限制的数。枚举在\(k\)这个位置填的是第几大的数,答案为\(\sum_{j=0}^{cnt-1}f_{n-i,j}=f_{n-i+1,cnt-1}\)。
  2. \(q_k<mx\)。假设在\(k\)这个位置填的数\(q_k<x<mx\)显然是不可能的。因为\(x\)是与\(q_k\)都是有限制的数,只能保持升序,所以\(x<q_k<mx\)。所以只能填\(>mx\)的数,答案依然是\(\sum_{j=0}^{cnt-1}f_{n-k,j}=f_{n-k+1,cnt-1}\)。

当序列已经不合法了就结束。

具体实现可以用树状数组。

代码:

#include<bits/stdc++.h>
#define ll long long
#define N 600005 using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;} const ll mod=998244353;
ll ksm(ll t,ll x) {
ll ans=1;
for(;x;x>>=1,t=t*t%mod)
if(x&1) ans=ans*t%mod;
return ans;
}
int n;
int q[N];
ll fac[N*2],ifac[N*2];
void pre(int n) {
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
ifac[n]=ksm(fac[n],mod-2);
for(int i=n-1;i>=0;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
} ll C(int n,int m) {return fac[n]*ifac[m]%mod*ifac[n-m]%mod;}
ll cal(int n,int k) {
if(k==0) return 1;
if(k==1) return n;
return (C(n+k,k)-C(n+k,k-1)+mod)%mod;
}
int low(int i) {return i&(-i);}
int tem[N];
void add(int v,int f) {for(int i=v;i<=n;i+=low(i)) tem[i]+=f;}
int query(int v) {
int ans=0;
for(int i=v;i;i-=low(i)) ans+=tem[i];
return ans;
} bool vis[N];
int main() {
pre(1200000);
int T=Get();
while(T--) {
ll ans=0;
n=Get();
for(int i=1;i<=n;i++) tem[i]=0;
for(int i=1;i<=n;i++) q[i]=Get();
int mx=0,cnt;
for(int i=1;i<=n;i++) {
if(q[i]>mx) {
mx=q[i];
cnt=n-q[i]-(i-1-query(q[i]-1));
}
(ans+=cal(n-i+1,cnt-1))%=mod;
int low=query(q[i]-1);
if(low!=i-1&&low!=q[i]-1) break;
add(q[i],1);
}
cout<<ans<<"\n";
}
return 0;
}

Loj #2719. 「NOI2018」冒泡排序的更多相关文章

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

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

  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. javascript split() 把一个字符串分割成字符串数组,类似于PHP的 explode()函数

    用法类似于框里的 例子:

  2. jieba 分词使用入门

    1. 介绍 JIEBA 是目前最好的 Python 中文分词组件,它主要有以下 3 种特性: 支持 3 种分词模式:精确模式.全模式.搜索引擎模式 支持繁体分词 支持自定义词典 import jieb ...

  3. 给 K8s API “做减法”:阿里巴巴云原生应用管理的挑战和实践

    作者 | 孙健波(天元)  阿里巴巴技术专家本文整理自 11 月 21 日社群分享,每月 2 场高质量分享,点击加入社群. 早在 2011 年,阿里巴巴内部便开始了应用容器化,当时最开始是基于 LXC ...

  4. 手写SpringMVC实现过程

    1. Spring Boot,Spring MVC的底层实现都是Servlet的调用. 2. Servlet的生命周期里面首先是类的初始化,然后是类的方法的调用,再次是类的销毁. 3. 创建一个spr ...

  5. C# convert between Image and Base64string

    static void ImageMSDemo(string picPath) { byte[] imageArray = System.IO.File.ReadAllBytes(picPath); ...

  6. 刷抖音太累,教你用Python把高颜值的小姐姐都爬下来慢慢看

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 星安果.AirPython 目 标 场 景 相信大家平时刷抖音短视频 ...

  7. 完美解决linux下vim在终端不能用鼠标复制的问题

    在vim 中输入 :set mouse=r 就行了,还不行的话 :set mouse=v

  8. 通过重复运行的Microsoft Flow由OAuth认证后获取Access Token并将其更新到实体记录

    我是微软Dynamcis 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...

  9. SAP MM MB5L 报表里的差异金额如何调整?

    SAP MM MB5L 报表里的差异金额如何调整? 5月3号,收到财务顾问提出的一个问题,说是MB5L报表里有差异. 如下查询条件, 报表结果里显示有差异, 经查,导致这个差异的原因之一是,一些物料批 ...

  10. gyp编译工具

    最近用到了 node-gyp 这个工具, 是node 社区对 google gyp 编译工具的一个封装, 使用 node-gyp 工具可以用C++为node 项目编写 addon. 了解了一下 goo ...