1529 排列与编码
给出两个长度为N的排列P1,P2,有着完全相同的元素。设这些元素所能组成的不同的排列数量共L个(L可能很大),按照字典序排序后,编码为 0 到 L - 1。其中P1的编码为R1,P2的编码为R2。求编码为:(R1 + R2) % L的排列是什么?
例如:P1 = {2, 2, 1},P2 = {2, 1, 2},则L = 3, R1 = 2, R2 = 1,那么需要求的排列编码为:(R1 + R2) % L = (2 + 1) % 3 = 0,对应的排列为:{1, 2, 2}。
输入
第1行:1个数N,表示排列的长度(1 <= N <= 50000)。
第2 -N + 1行:每行2个数,用空格分隔,对应P1[i]和P2[i](1 <=P[i] <= 10^9)。
(输入数据保证P1同P2有着完全相同的元素)。
输出
输出共n行,对应题目要求的排列。
输入样例
3
2 2
2 1
1 2
输出样例
1
2
2
分析:
对于这个问题,我并没有想出优雅的方法,而是比较暴力的使用高精计算,因此你最好选择Java 8,Python,Ruby,Go等支持FFT高精的语言来解题(计算中需要用到高精除法)。
假如没有重复的话,直接使用康托展开(康托展开_百度百科)便可以处理,有重复的话需要将计算结果除以当前数字出现的次数,考虑到大数运算的复杂度,整个算法是平方的。所以我们考虑用混合进制来表示大数,但每一位都需要用高精来存储,以便好好利用FFT的优势。
混合进制的可选方案也很多,在某一位上甚至可以使用5/2这类进制(方便处理重复字符的情况),但考虑整个计算除了要计算编码,还要根据编码反向计算排列,按照这种方式,很难做到一致,所以需要考虑无论是正向还是反向,都可以处理的一种进制,用阶乘进制来表示。
阶乘进制的第n位表示(n - 1)!,假设这一位的数字为k,则表示k*(n - 1)!。
第一步:使用康托展开,利用线段树或树状数组处理前缀和,可以求出某个排列的混合进制表示。
例如:1 3 1 2 2 1,处理结果为:
0 4 0 1 0 0 (后面的数字中有多少个比当前数字小)
3 1 2 2 1 1 (当前数字共有多少个,1有3个,所以第一位是3,但处理到第3位,第2个1时,由于前面的1已经减去,只剩下2个,所以第3位是2)
有了这两个计数,就好计算编码了,最终的结果是:
0 * (1 ) * (1 * 2 * 2 * 1 * 3) +
0 * (1) * ( 2 * 2 * 1 * 3) +
1 * (1 * 2) * ( 2 * 1 * 3) +
0 * (1 * 2 * 3) * (1 * 3) +
4 * (1 * 2 * 3 * 4) * (3) +
0 * (1 * 2 * 3 * 4 * 5)
= 312
这里计算的实际上是真实R的12倍,312