题意:
n(2*10^5)个元素的排列有n!种 用Perm(x)表示字典序第x的序列(从0开始) 用Ord(排列y)表示排列y的字典序 现在输入排列p和q 求 Perm([Ord(p)+Ord(q)]%n!)
思路:
容易想到 对于第i位p[i] 如果它是第d小的数字 那么说明比它小的d-1个数字所产生的全排列都已经计数过了
例子 35142 第4位是4 它是第2小的(1和3出现过了) 那么35124这种情况一定已经计数了
因此我们可以分别对于p和q找出序列是排第几的 也就是Ord(p)和Ord(q) 那么根据这个过程的逆过程就可以做出Perm了
现在的难点在于n!这个数字太大了 不能计算
但是我们发现 我们感兴趣的东西都是阶乘!! 因此我们只需要记录(n-1)! (n-2)! …… 出现的次数 做完Ord(p)和Ord(q)之后将这个次数数组像模拟高精度一样扫一遍 就可以做出[Ord(p)+Ord(q)]%n!的结果了
我在做“查询第几小的数字”这个功能使用了二分+树状数组 因此复杂度为O(nlog^2n)
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
using namespace std;
typedef long long LL;
#define N 200010
#define lowbit(x) (x&(-x))
int n;
int a[N], b[N];
int f[N], c[N];
inline void scand(int &ret) {
char c;
ret = 0;
while ((c = getchar()) < '0' || c > '9')
;
while (c >= '0' && c <= '9')
ret = ret * 10 + (c - '0'), c = getchar();
}
void add(int x, int y) {
while (x <= n) {
c[x] += y;
x += lowbit(x);
}
}
int sum(int x) {
int res = 0;
while (x) {
res += c[x];
x -= lowbit(x);
}
return res;
}
void init() {
memset(c, 0, sizeof (c));
for (int i = 1; i <= n; i++) add(i, 1);
}
int main() {
scand(n);
for (int i = 1; i <= n; i++) {
scand(a[i]);
a[i]++;
}
for (int i = 1; i <= n; i++) {
scand(b[i]);
b[i]++;
}
init();
for (int i = 1; i < n; i++) {
int les = sum(a[i] - 1);
f[n - i] += les;
add(a[i], -1);
}
init();
for (int i = 1; i < n; i++) {
int les = sum(b[i] - 1);
f[n - i] += les;
add(b[i], -1);
}
for (int i = 1; i < n; i++) {
f[i + 1] += f[i] / (i + 1);
f[i] = f[i] % (i + 1);
}
// printf("F:\n");
// for (int i = 1; i <= n; i++)printf("%d ", f[i]);
// printf("\n");
init();
int sml = 1;
for (int i = n - 1; i >= 1; i--) {
int l = 1, r = n, mid, tmp, ans = -1;
while (l <= r) {
mid = (l + r) / 2;
tmp = sum(mid - 1);
if (tmp <= f[i]) {
l = mid + 1;
ans = mid;
} else {
//ans = mid;
r = mid - 1;
}
}
if (ans == -1) {
ans = sml;
}
printf("%d ", ans - 1);
add(ans, -1);
while (!c[sml]) sml++;
// printf("C:\n");
// for (int i = 1; i <= n; i++)printf("%d ", c[i]);
// printf("\n");
// printf("sml %d\n", sml);
}
for (int i = 1; i <= n; i++) {
if (c[i]) {
printf("%d\n", i - 1);
break;
}
}
return 0;
}