Loading

康托展开

1转载注明:

康托展开

康托展开真是个神奇的东西。
除了本题用于求某排列的排名外,康托展开一般用于哈希,不过我也没做到过这样的哈希题。

2康托展开

先给你柿子。

\(ans=1+\sum_{i=1}^{n} A[i]\times(n-i)!\)

其中A[i]代表\(\sum_{j=i}^{n}[a[j] < a[i]]\)
怎么来理解这个柿子呢?想象构造出字典序比当前排列小的有几个排列
枚举到ii表示 1到i-1和原来的排列一样,i位肯定不一样,之后咋样都行。
既然到i位不一样,那么字典序大小其实就是取决于i位。很明显,第i位肯定要小于a[i]。然后只要把i后面小于a[i]的数交换到i位,后面随便排就行了。
很明显,这样枚举可以做到不重不漏。因为要求的是排名,所以ans+=1。
当然要用树状数组优化一下,复杂度是O(nlgn)的。

2.1C++98/11/14/17

#include<bits/stdc++.h>
using namespace std;
#define MAXN 1000005
#define rgt register
#define mod 998244353

int N, a[MAXN], fac, c[MAXN], ans;
char *p;

inline void read( rgt int &x ){
	x = 0; while( !isdigit(*p) ) ++p;
	while( isdigit(*p) ) x = x * 10 + ( *p & 15 ), ++p;
}

int main(){
	scanf( "%d", &N ), fac = 1;
	p = new char[N * 8 + 100],
	fread( p, 1, N * 8 + 100, stdin );
	for ( rgt int i = N; i; --i ) read(a[i]);
	for ( rgt int i = 1, s, j; i <= N; ++i ){
		for ( s = 0, j = a[i]; j; j -= j & -j ) s += c[j];
		ans = ( ans + 1ll * fac * s ) % mod, fac = 1ll * fac * i % mod;
		for ( j = a[i]; j <= N; j += j & -j ) ++c[j];
	} printf( "%d\n", ans + 1 );
	return 0;
}

2.2Pascal

var
n, fac, s, ans, i, j:longint;
a, c:array[1..1000000] of longint;
begin
    read(n); fac := 1; ans := 0;
    for i := n downto 1 do
    begin
        read(a[i]);
        c[i] := 0;
    end;
    for i := 1 to n do
    begin
        j := a[i]; s := 0;
        while j > 0 do
        begin
            s := s + c[j];
            j := j - ( j and -j ); 
        end;
        ans := ( QWORD(ans) + QWORD(fac) * QWORD(s) ) mod 998244353;
        fac := QWORD(fac) * QWORD(i) mod 998244353;
        j := a[i];
        while j <= n do
        begin
            c[j] := c[j] + 1;
            j := j + ( j and -j );
        end;
    end;
    writeln((ans + 1) mod 998244353);
end.

3逆康托展开

类似于进制转换,不断 % (n-i)!, /(n-1)!就可以得到A数组,然后就可以还原出原排列。

Update on 2019.7.23
昨天刚刚集训回来,于是就来填坑了

例题
这题十分好心地为我们省去了求出A数组的过程(否则要高精度除法?
问题说白了就是在每一个[i,n]区间内求K大值。可以使用权值线段树+二分来解决这一问题。这应该比较基础,所以看代码吧qaq。

3.1代码

没怎么卡常数,本来想搞zkw线段树非递归减小常数,但是懒。。。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 50005

int T, N, tr[MAXN << 2];

void Build( int c, int l, int r ){ //建树
	if ( l == r ) return tr[c] = 1, void();
	int mid((l + r) >> 1), ls(c << 1), rs(c << 1 | 1);
	Build( ls, l, mid ), Build( rs, mid + 1, r ),
	tr[c] = tr[ls] + tr[rs];
}

int Get( int c, int l, int r, int k ){ //找到k大值的同时删除k大值
	--tr[c]; if ( l == r ) return l;
	int mid((l + r) >> 1), ls(c << 1), rs(ls | 1);
	if ( tr[ls] < k ) return Get( rs, mid + 1, r, k - tr[ls] );//线段树上二分找到k大值
	return Get( ls, l, mid, k );
}

int main(){
	scanf( "%d", &T );
	while( T-- ){
		scanf( "%d", &N ), Build( 1, 1, N );
		for ( int i = 1, s; i <= N; ++i )
			scanf( "%d", &s ), printf( "%d%c", Get(1, 1, N, s + 1), "\n "[i < N] );
	} return 0;
}

4自己的代码:

康托展开:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ld long double
#define ll long long
#define ull unsigned long long
#define N 1000100
#define M number
using namespace std;

const int mod=998244353;

inline ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

struct FT{
	ll p[N<<2];
	
	inline ll lowbit(ll k){
		return k&(-k);
	}
	
	inline void add(ll k,ll x,ll n){
		for(int i=k;i<=n;i+=lowbit(i)) (p[i]+=x)%=mod;
	}
	
	inline ll ask(ll k){
		ll sum=0;
		for(int i=k;i;i-=lowbit(i)) (sum+=p[i])%=mod;
		return sum;
	}
};
FT ft;

ll jie[N];
ll n;
ll ans;

inline void prework(){
	jie[0]=1;
	for(int i=1;i<=N-99;i++) jie[i]=jie[i-1]*i%mod; 
}

int main(){
	prework();
	n=read();
	for(int i=1;i<=n;i++){
		ll x;x=read();
		ll sum=x-ft.ask(x-1)-1;
		(ans+=sum*jie[n-i])%=mod;
		ft.add(x,1,n);
	}
	printf("%lld\n",(ans+1)%mod);
	return 0;
}

逆康托展开

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ld long double
#define ll long long
#define ull unsigned long long
#define N 50002
#define M number
using namespace std;

inline ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

struct QST{
	int p[N<<2];
	
	inline void clear(){
		memset(p,0,sizeof(p));
	}
	
	inline void pushup(int k){
		p[k]=p[k*2]+p[k*2+1];
	}
	
	inline void build(int k,int l,int r){
		if(l==r){
			p[k]=1;
			return;
		}
		int mid=l+r>>1;
		build(k*2,l,mid);
		build(k*2+1,mid+1,r);
		pushup(k);
	}
	
	inline void add(int k,int l,int r,int po,int x){
		if(l==r){
			p[k]+=x;
			return;
		}
		int mid=l+r>>1;
		if(po<=mid) add(k*2,l,mid,po,x);
		else add(k*2+1,mid+1,r,po,x);
		pushup(k);
	}
	
	inline int getval(int rank,int k,int l,int r){
		if(l==r) return l;
		int mid=l+r>>1;
		if(rank<=p[k*2]) return getval(rank,k*2,l,mid);
		else return getval(rank-p[k*2],k*2+1,mid+1,r);
	}
};
QST qst;

int t;

int main(){
	t=read();
	while(t--){
		int n=read();
		qst.clear();
		qst.build(1,1,n);
		for(int i=1;i<=n;i++){
			int x=read();
			x++;
			int now=qst.getval(x,1,1,n);
			if(i==1) printf("%d",now);
			else printf(" %d",now);
			qst.add(1,1,n,now,-1);
		}
		printf("\n");
	}
	return 0;
}
posted @ 2021-03-24 21:22  hyl天梦  阅读(80)  评论(0编辑  收藏  举报