51nod1529 排列与编码

本文介绍了一种解决排列编码问题的方法,通过康托展开和阶乘进制来计算排列编码。当存在重复元素时,使用混合进制表示大数,并利用FFT进行高精度计算。算法通过分治策略将复杂度降低到nlogn^2。
摘要由CSDN通过智能技术生成

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 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值