题目链接:http://vjudge.net/problem/UVA-11525
大意:给定整数n和k,输出1-k的所有排列中,按照字典序从小到大排序后的第n个。其中,n = S1(k-1)! + S2(K-2)! + ... + Sk-1*1! + Sk *0!。
思路:
n的给出方式是极有规律的,很容易发现,Si即表示当前1-n中未被使用的第Si+1大的数。例如,n=4,S={2,2,1,0}。那么过程如下,首先找到1-4中未被使用的第3个数,结果为3,并把3标记为已使用;然后再找未被使用的第3个数,结果为4。重复这个过程,最终的序列为3,4,2,1。
那么思路也就有了,用一个线段树表示每个数的状态,1代表未使用,0代表已使用,区间和即表示这个区间中有多少个数未被使用。在查询时,用二分的思想,找到未被使用的第Si+1个数即可。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<set>
#include<map>
#include<vector>
#include<algorithm>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lc rt<<1
#define rc rt<<1|1
using namespace std;
const int maxn = 50000 + 5;
int sum[maxn<<2];
void PushUp(int rt)
{
sum[rt] = sum[lc] + sum[rc];
}
void build(int l, int r, int rt)
{
if (l == r) {
sum[rt] = 1;
return;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
PushUp(rt);
}
int query(int l, int r, int rt, int x)
{
if (l == r) {
sum[rt] = 0;
return l;
}
int m = (l + r) >> 1, ans = 0;
if (x <= sum[lc]) ans = query(lson,x);
else ans = query(rson,x-sum[lc]);
PushUp(rt);
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--) {
int n, x, ans;
scanf("%d",&n);
build(1,n,1);
for(int i = 1; i <= n; i++) {
scanf("%d",&x);
ans = query(1,n,1,x+1);
printf("%d%c",ans,i==n?'\n':' ');
}
}
return 0;
}