前言
这个东西也算比较常见吧,所以来记录下
作用
康托展开的实质是计算当前排列在所有由小到大全排列中的顺序
逆康托展开即已知在所有由小到大全排列中的顺序求排列
介绍
首先康托展开的公式
(x=a_i(n-1)!+a_{i-1}(n-2)!...a_{1}(0)!)
(a_i)表示原数的第(i)位在当前未出现的元素中有几个比他小
感觉这个证明挺简单的,不做说明
那么如何计算(a_i)呢,首先可以暴力(O(n^2))求出所有(a_i)
显然要优化
设当前值为(s),且其对应的(a_i)为(t),说明未出现的元素有(t)个元素比(s)小
那么说明在已经出现的元素中有(s-1-t)个元素比(s)小
利用线段求前面有多少个元素比自己小即可
实例
题目链接
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=1e6+5,mod=998244353,inf=0x3f3f3f3f;
int n,a[maxn];
int tree[maxn<<2];
int fac[maxn];
int query(int node,int L,int R,int l,int r){
if(L>R) return 0;
if(L<=l&&r<=R){
return tree[node];
}
int mid=(l+r)>>1,sum=0;
if(mid>=L) sum+=query(node<<1,L,R,l,mid);
if(mid<R) sum+=query(node<<1|1,L,R,mid+1,r);
return sum;
}
void update(int node,int l,int r,int pos){
if(l==r){
tree[node]=1;
return ;
}
int mid=(l+r)>>1;
if(mid>=pos) update(node<<1,l,mid,pos);
else update(node<<1|1,mid+1,r,pos);
tree[node]=tree[node<<1]+tree[node<<1|1];
}
int main(){
scanf("%d",&n);
fac[0]=1;
for(int i=1;i<=n;i++){
fac[i]=1ll*fac[i-1]*i%mod;
}
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
ll ans=1;
for(int i=1;i<=n;i++){
ans=(ans+1ll*(a[i]-query(1,1,a[i]-1,1,n)-1)*fac[n-i])%mod;
update(1,1,n,a[i]);
}
printf("%lld
",ans);
return 0;
}
逆康托展开其实就是一个相反的操作
实例
题目链接
题目思路
就是逆操作,权值线段树上二分即可
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=1e6+5,mod=998244353,inf=0x3f3f3f3f;
int n,a[maxn];
int tree[maxn<<2];
int query(int node,int l,int r,int val){
tree[node]--;
if(l==r){
return l;
}
int mid=(l+r)>>1,ans;
if(tree[node<<1]>=val) ans=query(node<<1,l,mid,val);
else ans=query(node<<1|1,mid+1,r,val-tree[node<<1]);
return ans;
}
void build(int node,int l,int r){
if(l==r){
tree[node]=1;
return ;
}
int mid=(l+r)/2;
build(node<<1,l,mid);
build(node<<1|1,mid+1,r);
tree[node]=tree[node<<1]+tree[node<<1|1];
}
int main(){
int _; scanf("%d",&_);
while(_--){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
build(1,1,n);
for(int i=1;i<=n;i++){
printf("%d%c",query(1,1,n,a[i]+1),i==n?'
':' ');
}
}
return 0;
}