将一个排列映射到一个数的方法就叫做康托展开。它的具体做法是这样的,对于一个给定的排列{ai}(i=1,2,3...n),对于每个ai求有多少个aj,使得j>i且ai>aj,简单来说就是求ai后面有多少数比ai小,假设我们求出来了这样的排列的一个对应数组{bi},其中bi就是ai后面有多少个数比它小。那么这个排列对应的康托展开即为∑bi*(n-i)!.

ai={1 3 5 4 2}

bi={0 1 2 1 0} 对应的排列数  0*4!+1*3!+2*2!+1*1!+0*0!.

bi可以通过树状数组在O(nlogn)里得到。

至于逆康托展开,就是已知bi,反求出ai,即知道了{0 1 2 1 0}.

做法也是类似的,我们看第一个数是0,表示后面有0个比它小的数,那么它必然是1.然后看第二个数是1,表示这个数在除去1 后面有1个比它小的数,那么这个数就是3,然后2,表示这个数后面在除去1,3,之后有2个比它小的数,所以它是5,以此往下。

一个O(nlogn)的想法是这样的,建立一棵平衡树,把1~n都插进去,每次查找第(bi+1)大的数,即为第i个数,然后把这个数删掉,以此往复。

但手写一个支持找第k大的平衡树比较慢,另外一个想法是在bit里把每个数都置1,然后二分出第一个使得前缀和为bi+1的数,这样做的复杂度就变成了O(nlog^2n).

#pragma warning(disable:4996)
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std; #define maxn 210000 int bit[maxn];
int n; void upd(int i)
{
while (i <= n){
bit[i] += 1;
i += i&(-i);
}
} void dec(int i)
{
while (i <= n){
bit[i] -= 1;
i += i&(-i);
}
} int cal(int i)
{
int ret = 0;
while (i > 0){
ret += bit[i];
i -= i&(-i);
}
return ret;
} int solve(int x)
{
int l = 1, r = n+1;
while (l < r)
{
int mid = (l + r) >> 1;
int tx = cal(mid);
if (tx < x) l = mid + 1;
else r = mid;
}
return l;
} int a[maxn];
int b[maxn];
int c[maxn];
int va[maxn];
int vb[maxn];
int vc[maxn]; int main()
{
while (cin >> n)
{
for (int i = 0; i < n; ++i){
scanf("%d", a + i); a[i]++;
}
for (int i = 0; i < n; ++i){
scanf("%d", b + i); b[i]++;
}
memset(bit, 0, sizeof(bit));
for (int i = n - 1; i >= 0; --i){
va[i] = cal(a[i]);
upd(a[i]);
}
memset(bit, 0, sizeof(bit));
for (int i = n - 1; i >= 0; --i){
vb[i] = cal(b[i]);
upd(b[i]);
}
reverse(va, va + n);
reverse(vb, vb + n);
memset(vc, 0, sizeof(vc));
int carry = 0;
for (int i = 1; i < n; ++i){
vc[i] = (carry + va[i] + vb[i]) % (i + 1);
carry = (carry + va[i] + vb[i]) / (i + 1);
}
reverse(vc, vc + n);
memset(bit, 0, sizeof(bit));
for (int i = 1; i <= n; ++i){
upd(i);
}
for (int i = 0; i < n; ++i){
c[i] = solve(vc[i]+1);
dec(c[i]);
}
for (int i = 0; i < n; ++i){
if (i) printf(" ");
printf("%d", c[i]-1);
}
puts("");
}
return 0;
}

最新文章

  1. ICEM(2)—机翼翼稍网格绘制
  2. ORA-00600: internal error code, arguments: [kcblasm_1], [103], [], [], [], [], [], []
  3. QT连接Linux mysql注意
  4. CoreGraphics QuartzCore CGContextTranslateCTM 用法
  5. 6.使用AngularJS模板来创建视图
  6. vba 工作案例1
  7. 例题:for循环迭代法。一个棋盘有n个格子,第一个格子有一粒米,第二个格子有两粒米,第三个格子有四粒米,依次类推,第n个格子里有多少粒米,棋盘里一共有多少粒米。
  8. 解决安装rpm时lib冲突:libstdc++-2-libc6.1-1-2.9.0.so from install of compat-libstdc++-7.3-2.96.118 conflicts with file from ...
  9. C#获取汉字拼音
  10. work6
  11. linux系统安装(虚拟机以及linux的下载与安装)
  12. 应用程序框架实战十三:DDD分层架构之我见(转)
  13. java大数 斐波那契数列
  14. 防止ajax重复提交
  15. python爬虫动态html selenium.webdriver
  16. mysql删除多个重复数据,多个字段添加唯一性索引
  17. Windows API教程文件系统
  18. PHP 3种方法实现采集网站数据
  19. Majority Element(169) &amp;&amp; Majority Element II(229)
  20. [Oracle]跨越 DBLINK 访问表时,数据缓存在何处的Data Buffer 中?

热门文章

  1. Cube HDU - 1220(思维)
  2. [Uva1642]魔法Gcd(数论)
  3. Linux命令之---pwd
  4. Elastic Search和Kibana入门
  5. json对象数据列数
  6. SXCPC2018 nucoj1999 占领城市
  7. 嵌入式tcpip
  8. Appium的三种等待时间设置方法
  9. Java学习4之抽象类
  10. [转]Jupyter NoteBook 的快捷键使用指南