【JZOJ7280】序排速快

【JZOJ7280】序排速快

by AmanoKumiko

Description

定义一次泡冒为:

function bubblesort(A)
	for i = 1length(A) − 1 do
		if Ai > Ai+1 then
			swap(Ai,Ai+1)
		end if
	end for
end function

即, 从该数组的第一个位置开始, 设当前进行到的位置为i , 若Ai>Ai+1则交换。定义一次序排速快为:

function quicksort(A)
	if length(A) = 1 then
		return
	end if
	while no partition points exist in A do
		cnt ← cnt + length(A)
		bubblesort(A)
	end while
	divide A at all partition points; do quicksort at each piece
end function

即, 对于当前递归到的区间[l,r], 定义一个位置i为分割点, 当且仅当x[l,i],y[i+1,r],Ax<Ay。序排速快的过程是这样的, 对于当前的区间[l,r], 若该区间长度为 1 , 直接返回;否则, 若该区间中存在分割点, 则找出所有分割点, 递归两两相邻分割点中的区间。即, 若当前递归区间为[l,r] , 分割点为 p1,p2,···,pk,pi[l,r] 则递归[l,p1],[p1+1,p2],...,[pk1+1,pk],[pk+1,r]。若不存在分割点, 则执行多次泡冒, 每一次泡冒将 cnt 的值加上rl+1,直至出现分割点, 执行递归。给出两个正整数 L,R , 对所有的 n[L,R] 分别求出所有长度为n 的排列的 cnt 值之和对998244353取模的结果。

Input

一行两个正整数L,R,意义同题目描述

Output

为减少输出量,仅输出一个整数表示所有答案的异或和

Sample Input

2 8

Sample Output

920329

Data Constraint

对于 100% 的数据,2L,R107

Solution

(1)发现分割点的条件可以改成,对于i[1,i]的数都已经在i左侧出现

(2)分割点i出现时,它左侧和右侧的分割点一定已经出现

(3)一个点的贡献就是它递归的层数

不妨对每个分割点i单独算贡献,设分割点i出现时间为ti,那么答案为i=1nmax(ti,ti1)

由于一次冒泡可以使不在原来位置的点移动一格,设小于等于i的最大的数的位置为pi,那么移动次数为pii

所以答案变成i=1nmax(pi1i+1,pii)

分开算贡献:

对于ti>=ti1i=1nj=inCninj(nj)!(j1)!(ji)i枚举分割点,j枚举pi,即[i+1,n]选出一些放在j右边,剩下的放左边,同时j两边都是随便排列

对于ti1>ti,同理得i=1nj=inCnjni(nj)!(j1)!(ji+1)(i1),由于j此时放的是[1,i1]中的数,所以有i1种选择

加起来得到i=1nj=inCnjni(j1)!(nj)!(iji2+i1)

i=1nj=inCnjni(j1)!(nj)!(i2+i1)i=1nj=inCnjni(j1)!(nj)!ij

第一个式子变换后得到i=1n(i2+i1)(ni)!(i1)!Cni

n!i=1ni2+i+1i

第二个式子变换后得到i=1ni(i!)(ni)!Cn+1i+1

(n+1)!i=1nii+1

前缀和预处理一下随便算

Code

#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(long long i=a;i<=b;i++)
#define LL long long
#define N 10000010
#define mo 998244353

LL sum[N],fac[N],frac[N][2],L,R,inv[N];

int main(){
	freopen("tros.in","r",stdin);
	freopen("tros.out","w",stdout);
	fac[1]=inv[1]=1;
	F(i,1,N-10){
		inv[i+1]=mo-(mo/(i+1))*inv[mo%(i+1)]%mo;
		fac[i+1]=fac[i]*(i+1)%mo;
		frac[i][0]=(frac[i-1][0]+i*inv[i+1]%mo)%mo;
		frac[i][1]=(frac[i-1][1]+(-i*i%mo+i-1+mo)%mo*inv[i]%mo)%mo;
		sum[i]=(fac[i+1]*frac[i][0]%mo+fac[i]*frac[i][1]%mo)%mo^sum[i-1];
	}
	scanf("%lld%lld",&L,&R);
	printf("%lld",sum[R]^sum[L-1]);
	return 0;
}
posted @   冰雾  阅读(77)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示