UVA 11525 - Permutation(BIT+二分)

题目链接 https://cn.vjudge.net/problem/UVA-11525

【题意】
给定整数k,输出1~k的所有排列中,按照字典序从小到大排序后的第n个(编号从0开始)由于n可能很大,本题用k个整数S[1],S[2]…S[k]来间接给出n,方式如下
n=S[1]×(k-1)!+S[2]×(k-2)!+…+S[k]×0!

【输入格式】
多组输入,第一行为数据组数T(T<=10)每组数据第一行为一个整数k(1<=k<=50000)第二行包含k个整数S[i](0<=S[i]<=k-i)

【输出格式】
对每组数据,输出满足题意的1~k的一个全排列

【思路】
做这道题之前首先要懂一个关于全排列的知识点就是康托展开,用于计算第几个全排列是多少,或者计算某个全排列排在第几位。详细讲解见百度百科:康托展开
然后这道题目中给出的式子刚好就是康托展开式,其中S[i]表示的就是这个排列里面第i个位置的右边有几个数字比它自己小,比如说排列34152,那么s[2]=2,因为在第二位的4右边有2个数字比4要小(1,2),根据这个性质就不难求出原来的排列是多少了。

我们可以先把1~k都放入一个有序集合中,然后从高位向低位考虑,也就是从S[1]~S[k]考虑,每次找出一个最小值x,使得x>S[i],那么在答案中这一位上就应该是x了。可以用树状数组实现这个有序集合,然后二分查找每个x的值.

#include<bits/stdc++.h>
using namespace std;

const int maxn=50005;

int n;
int a[maxn];
int bit[maxn];
vector<int> ans;

void add(int i,int x){
    while(i<=n){
        bit[i]+=x;
        i+=i&-i;
    }
}

int sum(int i){
    int ans=0;
    while(i>0){
        ans+=bit[i];
        i-=i&-i;
    }
    return ans;
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        memset(bit,0,sizeof(bit));
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            add(i,1);
        }
        ans.clear();
        for(int i=1;i<=n;++i){
            int le=0,ri=n;
            while(le+1<ri){
                int mid=(le+ri)>>1;
                if(sum(mid)>a[i]) ri=mid;
                else le=mid;
            }
            add(ri,-1);
            ans.push_back(ri);
        }
        for(int i=0;i<ans.size();++i) printf("%d%c",ans[i],i+1==ans.size()?'\n':' ');
    }
    return 0;
}
posted @ 2018-08-11 22:41  不想吃WA的咸鱼  阅读(178)  评论(0编辑  收藏  举报