其他分享
首页 > 其他分享> > 康托展开

康托展开

作者:互联网

公式:\(a_{n}*(n-1)!+a_{n-1}*(n-2)!+……+a_{1}*0!\)
题目:
洛谷P5367 【模板】康托展开
题目描述
求 \(1\sim N\) 的一个给定全排列在所有 \(1\sim N\) 全排列中的排名。结果对 \(998244353\) 取模。

输入格式
第一行一个正整数 \(N\)。

第二行 \(N\) 个正整数,表示 \(1\sim N\) 的一种全排列。

输出格式
一行一个非负整数,表示答案对 \(998244353\) 取模的值。

输入输出样例
输入
3
2 1 3
输出
3
输入
4
1 2 4 3
输出
2
说明/提示
对于\(10\%\)数据,\(1\le N\le 10\)。
对于\(50\%\)数据,\(1\le N\le 5000\)。
对于\(100\%\)数据,\(1\le N\le 1000000\)。
对于这道题,康托展开的公式中\(a[i]\)是数列中从后往前数第\(1\sim i-1\)个数中比第i个数小的数的个数,整个运算过程也都是从后往前进行。
证明:
数列的第一个数,即最后一个被运算的数,当这个位置被确定时,后面的排列还有\((n-1)!\)种方式,而确保排在这种排列前面的排列一定是数比这个数小的数,共\(a[n]\)个,即它一定排在\(a[n]*(n-1)!\)种排列方式后,计算下一个位置时,同理,但因为前面的数已经确定了,所以\(a[i]\)只考虑它后面的数,其中求比这位数小数的个数可以用权值线段树。
代码:

#include<iostream>
#define mod 998244353
#define int long long
using namespace std;
int n;
int a[1000010];
struct node{
	int l,r;
	int sum;
}tree[4000010];
int tot=1;
void build(int i,int l,int r){
	tree[i].l=l;
	tree[i].r=r;
	if(l==r){
		tree[i].sum=0;
		return ;
	}
	int mid=(l+r)/2;
	build(i*2,l,mid);
	build(i*2+1,mid+1,r);
	tree[i].sum=0;
}
void add(int i,int p){
	tree[i].sum++;
	if(tree[i].l==tree[i].r){
		return ;
	}
	if(tree[i*2].r>=p){
		add(i*2,p);
	}
	else{
		add(i*2+1,p);
	}
	return ;
}
int search(int i,int l,int r){
	if(tree[i].l>=l&&tree[i].r<=r){
		return tree[i].sum;
	}
	if(tree[i].r<l||tree[i].l>r){
		return 0;
	}
	int s=0;
	if(tree[i*2].r>=l){
		s+=search(i*2,l,r);
	}
	if(tree[i*2+1].l<=r){
		s+=search(i*2+1,l,r);
	}
	return s;
}
int ans=0;
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	build(1,1,n);
	for(int i=n-1;i>=1;i--){
		add(1,a[i+1]);
		tot=tot*(n-i)%mod;
		ans=(ans+tot*search(1,1,a[i]-1)%mod)%mod;
	}
	cout<<ans+1;
	return 0;
} 

标签:le,return,int,sum,tree,add,展开,康托
来源: https://www.cnblogs.com/z-2-we/p/16171342.html