
链接:https://codeforces.com/problemset/problem/501/D
思路:首先看一下康拓展开的表达式,排列的序列数X与排列的关系:
x =
其中$a_i$指的是位置i后面小于$a_i$值的个数
那么我们可以算出两个排列的康拓展开,因为要对n!取模,好像必须要高精度了,这时候一个巧妙的方法来了,我们可以模拟进制的高精度取模方法,不断进位然后取模,给阶乘也弄一个类似的,不必算出两个排列对应x的和,而是算出每一项的系数,然后从最低位开始向上加,进位(最后一个进位舍掉,相当于取模),最后结果的系数就是最终排列的每一项了。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
|
using namespace std;
const int maxn = 2e5 + 5;
int c[maxn], f[maxn]; int n;
int (int x){ return x & (-x); }
void add(int x, int d){ while(x < maxn){ c[x] += d; x += lowbit(x); } }
int query(int x){ int res = 0; while(x){ res += c[x]; x -= lowbit(x); } return res; }
int main(){ scanf("%d", &n); for(int i = 1; i <= n; i++) add(i, 1); for (int i = 1; i <= n; ++i) { int x; scanf("%d", &x); x++; add(x, -1); f[n - i + 1] = query(x); } for(int i = 1; i <= n; i++) add(i, 1);
for(int i = 1; i <= n; ++i) { int x; scanf("%d", &x); x++; add(x, -1); f[n - i + 1] += query(x); }
for(int i = 1; i <= n; i++){ f[i + 1] += f[i] / i; f[i] %= i; }
for(int i = 1; i <= n; i++) add(i, 1);
for(int i = n; i >= 1; i--){ int lb = 1, ub = n, ans = 1; while(ub >= lb){ int mid = lb + ub >> 1; if(query(mid) >= f[i] + 1){ ub = mid - 1; ans = mid; } else lb = mid + 1; } add(ans, -1); cout << ans - 1 << ' '; } cout << 'n'; return 0; }
|
近期评论