Codeforces Round #285 (Div. 2) D. Misha and Permutations Summation 康托展开 树状数组+二分

本文深入探讨了一种独特的排列组合问题,即如何将两个特定排列的序号相加并取模,得到一个新的排列。通过实例分析,详细介绍了求解过程,包括将排列转换为康托展开形式、使用线段树加速判断和二分查找优化等关键步骤。文章旨在为读者提供一种高效解决此类问题的方法,同时展示了在实际应用中排列组合与模运算的巧妙结合。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

D. Misha and Permutations Summation
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Let's define the sum of two permutations p and q of numbers 0, 1, ..., (n - 1) as permutation , where Perm(x) is the x-th lexicographically permutation of numbers 0, 1, ..., (n - 1) (counting from zero), and Ord(p) is the number of permutation p in the lexicographical order.

For example, Perm(0) = (0, 1, ..., n - 2, n - 1), Perm(n! - 1) = (n - 1, n - 2, ..., 1, 0)

Misha has two permutations, p and q. Your task is to find their sum.

Permutation a = (a0, a1, ..., an - 1) is called to be lexicographically smaller than permutation b = (b0, b1, ..., bn - 1), if for some k following conditions hold: a0 = b0, a1 = b1, ..., ak - 1 = bk - 1, ak < bk.

Input

The first line contains an integer n (1 ≤ n ≤ 200 000).

The second line contains n distinct integers from 0 to n - 1, separated by a space, forming permutation p.

The third line contains n distinct integers from 0 to n - 1, separated by spaces, forming permutation q.

Output

Print n distinct integers from 0 to n - 1, forming the sum of the given permutations. Separate the numbers by spaces.

Sample test(s)
Input
2
0 1
0 1
Output
0 1
Input
2
0 1
1 0
Output
1 0
Input
3
1 2 0
2 1 0
Output
1 0 2

  1-N,给你两个排列,把这两个排列在全排列中的序号加起来,对N!取模得到一个数k,输出N个数的第k个排列。

  做这个题又让我回顾了一下以前做过的一个线段树加速康托展开,因为每次都要判断还未出现过的比当前数小的数有几个,如果一个一个数插入,用线段树或者树状数组判断当前已经出现了几个比这个数小的,复杂度是O(lgn)。

  首先要把两个排列化成(p1[n-1]+p2[n-1])*(n-1)!+(p1[n]+p2[n])*n!+...(p1[0]+p2[0])*0!的形式,因为系数p[n]不能大于n,单独算p1,p2不会超,两个加起来可能就会超了,不符合这种形式,所以要进位。p1[0]和p2[0]肯定都是0,从1开始,如果p1[i]+p2[i]大于i,那么就把i+1的系数加一,i的系数减去i+1,到最高位也就是(p1[n-1]+p2[n-1])*(n-1)!,这里不用再进位,因为要对N!取模,也就相当于(p1[n-1]+p2[n-1])对n取模。

  到这里得到了一个符合条件的康托展开式,最后就是把这个式子化成对应的排列。思路是利用树状数组+二分,先初始化一个每位都是1的树状数组,每次对p[i]进行二分查找位置,找到对应的位置,这个位置之前有p[i]个1,并且这个位置也是1。再把这个位置的1减掉。复杂度O(n*lgn*lgn)。

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;

const int MAXN=200010;

int N,a[MAXN],b[MAXN],c1[MAXN],c2[MAXN],c3[MAXN],p[MAXN],ans[MAXN];

int lowbit(int x){
    return x&(-x);
}

void add(int* c,int i,int v){
    while(i<MAXN){
        c[i]+=v;
        i+=lowbit(i);
    }
}

int getsum(int* c,int i){
    int ret=0;
    while(i){
        ret+=c[i];
        i-=lowbit(i);
    }
    return ret;
}

int bsearch(int pos,int l,int r){    //(l,r]
    while(l<r){
        int mid=l+(r-l)/2,n=getsum(c3,mid);
        if(n<pos) l=mid+1;
        else r=mid;
    }
    return r;
}

int main(){
    freopen("in.txt","r",stdin);
    while(scanf("%d",&N)!=EOF){
        for(int i=0;i<N;i++) scanf("%d",&a[i]);
        for(int i=0;i<N;i++) scanf("%d",&b[i]);
        memset(c1,0,sizeof(c1));
        memset(c2,0,sizeof(c2));
        memset(c3,0,sizeof(c3));
        for(int i=1;i<=N;i++) add(c3,i,1);
        for(int i=0;i<N;i++){
            int n=getsum(c1,a[i]+1);
            p[N-i-1]=a[i]-n;
            add(c1,a[i]+1,1);
        }
        for(int i=0;i<N;i++){
            int n=getsum(c2,b[i]+1);
            p[N-i-1]+=b[i]-n;
            add(c2,b[i]+1,1);
        }
        for(int i=1;i<N-1;i++) if(p[i]>i){
            p[i+1]++;
            p[i]-=i+1;
        }
        p[N-1]%=N;
        for(int i=N-1;i>=0;i--){
            int n=bsearch(p[i]+1,0,N);
            ans[N-i-1]=n-1;
            add(c3,n,-1);
        }
        for(int i=0;i<N-1;i++) printf("%d ",ans[i]);
        printf("%d\n",ans[N-1]);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值