[洛谷]P5367【模板】康托展开

时间:2020-04-12
本文章向大家介绍[洛谷]P5367【模板】康托展开,主要包括[洛谷]P5367【模板】康托展开使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目

题目
康托展开,用于给你一个排列,让你求出这个排列是字典序的第几个全排列。
这么说比较抽象,我们举个例子。
比如给你一个排列\([2,1,3]\),这个排列是第几个全排列呢?
我们知道,1~3的排列有六种,是\([1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]\)\([2,1,3]\)按照字典序在第三个,所以是第三个全排列。
康托展开就是用来求这个东西的。

题解

比如,输入\([2,1,4,3]\),易得输出是8,那么怎么使用康托展开来做呢?我们一项一项来看。
s记录输出,初始为0。
首先第一项是2,第一项如果填1的话有3!=六种排列,所以第一位填2就肯定在6以上了,s+=6。
第二项是1,没什么用,不管它。
第三项是4,由于1,2已经填过了,所以4的前面只有3,第三项如果填3的话有1!=1种排列,s+=1。
最后一项有0!=1种排列,s+=1。
最后s=8。

最后给出通式:\(rank=A_n(n-1)!+A_{n-1}(n-2)!+...+a_10!\)
\(A_i=\sum_{j=i}^n[a[j]<a[i]]\)
那么怎么判断填过呢?笔者还没有想到线性的算法,只好写了个树状数组。

#include <iostream>
#define lowbit(x) x&(-x)

using namespace std;
typedef long long ll;
const ll N=1E6+10,M=998244353;
ll a[N],c[N],n;

void add(int x) {
	while(x<=n) {
		c[x]++;
		x+=lowbit(x);
	}
}

ll get(int x) {
	ll res=0;
	while(x) {
		res+=c[x];
		x-=lowbit(x);
	}
	return res;
}

int main() {
	cin>>n;
	for(int i=1;i<=n;i++)
	  cin>>a[i];
	ll s=1;
	ll frac=1;
	for(int i=n;i>=1;i--) { //反着递推,这样子好求阶乘取模
		ll k=get(a[i]);
		s=(s+frac*k)%M;
		frac=(frac*(n-i+1))%M;
		add(a[i]);
	}
	cout<<s;
	return 0;
}

原文地址:https://www.cnblogs.com/wyc06/p/12687596.html