codeforces 1443E-Long Permutation (树状数组 + 康托展开)

题目链接:https://codeforces.com/problemset/problem/1443/E

我们发现,排列的排名最多只会是 (10^10),所以最多只会修改 (15) 个位置
我们记录一下当前的排列排名是多少,然后只需要暴力进行逆康托展开,求出当前排名下的排列,暴力修改即可

求和用树状数组维护,暴力修改后 (15) 个位置即可

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;

const int maxn = 200010;

int n, q; 

ll c[maxn], fac[maxn];

void add(int x, ll k){
	for(; x <= n ; x += x & (-x)){
		c[x] += k;
	} 
}

ll query(int x){
	ll res = 0;
	for(; x ; x -= x & (-x)){
		res += c[x];
	}
	return res;
}


int v[20], a[20], p[maxn];

void change(ll sum){
	for(int i = 1 ; i <= 15 ; ++i) v[i] = 1;
	ll tmp = sum;
	
	for(int i = 1 ; i <= 15 ; ++i){
		int t = tmp / fac[15 - i];
		int cnt = 0;
		for(int j = 1 ; j <= 15 ; ++j){
			if(v[j]){
				if(cnt == t){
					a[i] = j;
					v[j] = 0;
					break;
				}
				++cnt;
			}
		}
		tmp %= fac[15 - i];
	}
	
	if(n < 15){
		int d = 15 - n;
		for(int i = 1 ; i <= n ; ++i){
			add(i, -p[i]);
			p[i] = a[i + d] - d;
			add(i, p[i]);
		}
	} else{
		int d = n - 15;
		for(int i = n - 14 ; i <= n ; ++i){
			add(i, -p[i]);
			p[i] = a[15 - (n - i)] + d;
			add(i, p[i]);
		}
	}
}

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

int main(){
	n = read(), q = read();
	for(int i = 1 ; i <= n ; ++i) add(i, i);
	for(int i = 1 ; i <= n ; ++i) p[i] = i;
	fac[0] = 1;
	for(int i = 1 ; i <= 15 ; ++i) fac[i] = fac[i - 1] * i;
	
	ll ans = 0;
	int op, x, y;
	for(int i = 1 ; i <= q ; ++i){
		op = read();
		if(op == 1){
			x = read(), y = read();
			printf("%lld
", query(y) - query(x - 1));
		} else{
			x = read();
			ans += x;
			change(ans);
		}
	}
	
	return 0;
}
原文地址:https://www.cnblogs.com/tuchen/p/14024677.html