[郑州培训2012] 数列的排序(康托展开)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_33831360/article/details/102528152

cogs提交

对于正整数N,则1到N这N个数可以构成N!种排列,把这些排列按照字典序从小到大列出。

如N=3时,列出1 2 3,1 3 2,2 1 3,2 3 1,3 1 2,3 2 1六个排列。

现在,给你排列{Pi},请你计算它后面的第K个排列{Qi}。

注意:这N!个排列是循环的,例如3 1 2后面的第2个排列是1 2 3。

类别

数量

数据描述

1

3

1<=N<=10   1<=K<=100000

2

3

1<=N<=1000   1<=K<=100000

3

3

1<=N<=1000   1<=K<=10^18

4

1

1<=N<=100000   1<=K<=10^18

自然的想法是求康拓展开,直接加k后逆康拓展开,但是n太大了,康拓结果根本存不了。

但是k不大,其实20个数就有20!个排列,这很大了,所以我们取出后20个数,按大小映射成0,1,2,3...19,康拓逆康拓 再映射回来就行,前面的数不用变。

但有特殊情况,后面20个数本身排列就字典序大,+K后超过了n!,这时候把它变成最后的排列19,18....0.映射回来,可以用STL的 next_permutation求整个序列的下一个序列,之后再处理一下后20位就可以了。

#include <iostream>
#include <cstdio>
#include <algorithm>
 
using namespace std;
const int MAXN=10;
 
typedef long long LL;
 
LL k,fac[30];
bool v[30];
int n,b[30],a[101000];
 
LL cantor(int *a,int n) {
    LL tmp = 0;
    for (int i = 0; i < n; i++) {
      int k = 0;
      for (int j = i+1; j < n; j++) if(a[j] < a[i]) k++;
      tmp += fac[n-i-1]*k;
    }
    return tmp+1;
}
 
void Inv_cantor(int n,LL m) {
    --m;
    for (int i = 0; i < n; i++) v[i] = 0;
    for (int i = n-1; ~i; i--) {
      int k = m/fac[i],j; m %= fac[i];
      for (j = 0; j < n && k; j++) if(!v[j]) k--;
      while(v[j]) j++;
      v[b[n-i-1] = j] = true;
    }
    for (int i = 0; i < n; i++) ++b[i];
}
 
int c[30];
void Get() {
    for (int i = 0; i < 20; i++) c[i] = a[n-20+i];
    sort(c,c+20);
    for (int i = 0; i < 20; i++) b[i] = lower_bound(c,c+20,a[n-20+i])-c+1;
}
 
void Re_Get() {
    for (int i = 0; i < 20; i++) a[n-20+i] = c[b[i]-1];
}
 
inline LL Mod(LL x,LL p) {return x%p ? x%p : p;}
 
int main(int argc, char const *argv[]) {
    freopen("sortb.in","r",stdin); freopen("sortb.out","w",stdout);
    scanf("%d%lld",&n,&k);
    for (int i = 0; i < n; i++) scanf("%d",a+i);
    fac[0] = 1;
    for (int i = 1; i <= 20; i++) fac[i] = fac[i-1]*i;
    if(n <= 20) {
      Inv_cantor(n,Mod(cantor(a,n)+k,fac[n]));
      for (int i = 0; i < n; i++) printf("%d ",b[i]);
    } else {
      Get();
      LL nt = cantor(b,20);
      while(nt+k > fac[20]) {
         for (int i = 0; i < 20; i++) a[n-20+i] = c[19-i];
         next_permutation(a,a+n);
         k -= fac[20]-nt+1;
         nt = 1; Get();
      }
      Inv_cantor(20,nt+k);
      Re_Get();
      for (int i = 0; i < n; i++) printf("%d ",a[i]);
    }
    return 0;
}

 

猜你喜欢

转载自blog.csdn.net/qq_33831360/article/details/102528152