首页 > 其他分享 >【NOI2018】冒泡排序

【NOI2018】冒泡排序

时间:2023-03-06 19:56:03浏览次数:47  
标签:排列 int res mo mi 冒泡排序 NOI2018

【NOI2018】冒泡排序

Description

最近,小 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\) 取模的结果。

Input

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

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

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

Output

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

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

Sample Input

1
3
1 3 2

Sample Output

3

Data Constraint

\(n\le 6*10^5,\sum n\le 2*10^6\)

Solution

打表可以发现,好的排列要求LIS长度\(\le 2\)

证明读者自证不难

因为有个字典序的限制,所以不妨设\(f(i,j)\)表示前\(i\)个数,最大值为\(j\)时,后面\(n-i\)个数的方案数

注意\(i\le j\)

显然\(f(i,j)\)可以转移到\(f(i,k)\)(\(k>j\))

对于\(k<j\),只能填能填的数中最小的,因为不填的话,填最小数时LIS就会变成\(3\)了

所以\(f(i,j)\to f(i,k)\)(\(k\ge j\))

那么\(f(i,j)=\sum_{k=i-1}^{j}f(i-1,k)\)

化简一下就是\(f(i,j)=f(i-1,j)+f(i,j-1)\)

于是可以转换成格路计数

现在回到字典序的限制上

枚举一个lcp之后

假设当前填的最大数是\(mx\),能填的最小数是\(mi\)

当\(q_i=mi\)时:

填\(mi\)显然不合法

填\(mi<x<mx\),那么之后LIS一定不小于\(3\)

填\(x>mx\),可以任填

当\(mi<q_i<mx\)时:

类似分析得到,只能填\(x>mx\)

当\(q_i>mx\)时:

可以任填\(x>q_i\)

对于方案数的计算,就是一列组合数求和相减,容易计算

Code

#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define mo 998244353
#define N 1200010

int T,n,q[N];
int fac[N],ifac[N];

int mod(int x){return x>=mo?x-mo:x;}

int mi(int x,int y){
	int res=1;
	for(;y;x=1ll*x*x%mo,y>>=1)if(y&1)res=1ll*res*x%mo;
	return res;
}

int C(int x,int y){return x<y||x<0||y<0?0:1ll*fac[x]*ifac[y]%mo*ifac[x-y]%mo;}

int calc(int x,int y){return y<=x?mod(C(n*2-x-y,n-x)-C(n*2-x-y,n-x-1)+mo):0;}

int vis[N];

int main(){
	fac[0]=1;
	F(i,1,N-10)fac[i]=1ll*fac[i-1]*i%mo;
	ifac[N-10]=mi(fac[N-10],mo-2);
	Fd(i,N-11,0)ifac[i]=1ll*ifac[i+1]*(i+1)%mo;
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		F(i,1,n)scanf("%d",&q[i]);
		int ans=0,mx=0,mi=1;
		F(i,1,n){
			mx=max(mx,q[i]);
			ans=mod(ans+calc(mx+1,i-1));
			vis[q[i]]=1;
			while(vis[mi])mi++;
			if(mi<q[i]&&q[i]<mx)break;
		}
		printf("%d\n",ans);
		F(i,1,n)vis[i]=0;
	}
	return 0;
}

标签:排列,int,res,mo,mi,冒泡排序,NOI2018
From: https://www.cnblogs.com/AmanoKumiko/p/17185137.html

相关文章

  • 冒泡排序
    冒泡排序对N个数据进行排序,共进行N-1轮排序,每一轮都从第一个数据向后面比较(假如从小向大排列),若前面的数据大于后面的数据,则交换位置,再让第二个数据与第三个比较,以此类推......
  • java-数组,冒泡排序19
    packagecom.demo.data;publicclassarr{publicstaticvoidmain(String[]args){int[]arr={11,22,33,44,999};intmax=m(arr);......
  • 冒泡排序及其优化
    importjava.util.Arrays;publicclassbobbleSort{publicstaticvoidmain(String[]args){int[]arr={2,6,3,7,4,1,8,5,0,9};//......
  • LabVIEW|冒泡排序的实现
    冒泡排序简述:描述来自于大的泡泡总是先浮到水面。考虑一下,我们平时怎么给东西排序,比如有一堆苹果,需要我们按照个头从大到小排序。冒泡排序就是:先比较最右面两个苹果,如果左边......
  • 使用qsort函数实现冒泡排序(函数指针的运用)
    //此程序的本质:完全理解qsort函数的传参的原则////实现思路:因为我们是模拟qsort函数//所以我们要自己创造一个:比较数据的函数:cmp_int//因此必须有一个函数指针来接收这......
  • c语言学习记录 冒泡排序
    #include<stdio.h>#include<string.h>#define_CRT_SECURE_NO_WARNINGS1voidbubble_sort(intarr[],intsz){ inti=0; //排序次数 for(i=0;i<sz-1;i+......
  • (优化/未优化)冒泡排序
    #include<stdio.h>//还可以优化voidbubble_sort(intarr[],intsize){for(inti=0;i<size-1;i++){//共有size-1趟冒泡排序for(intj=0;j<size-1-i;......
  • 冒泡排序
    点击查看代码publicstaticvoidmain(String[]args){int[]arr={9,4,3,7,8,2};inttemp;//从小到大排序,每次把最小的移到最......
  • 简单的冒泡排序
    #define_CRT_SECURE_NO_WARNINGS1#include<stdio.h>#include<string.h>voidbouble_sort(intarr[],intsz){//确定冒泡排序的趟数inti=0;f......
  • Java的学习(冒泡排序和稀疏数组)
    1.比较数组中,两个相邻的元素,如果第一个数比第二个数大,我们就交换他们的位置2.每一次比较,都会产生一个最大或者最小的数字;3.下一轮则可以减少一次排序4.依次循环,直到结束......