题意:
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; }
时间: 2024-09-29 16:51:45