题目:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2520
找个简单的例子,k=3的时候:
2! 1! 0!
0 0 0 0 123
1 0 1 0 132
2 1 0 0 213
3 1 1 0 231
4 2 0 0 312
5 2 1 0 321
n S1 S2 S3
发现木有,有规律! 从最高位开始,第i位是使sum(1,j)==(Si + 1) 的最小j,sum只针对没用过的数。
所以可以用树状数组加二分,二分j;
也可以用线段树,单点存区间数字个数和,每次查询一个数,和节点的个数和比较决定往左还是往右,求得答案后,再单点更新。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<t;i++)
#define clr(a) memset(a,0,sizeof(a))
#define N 50005
int t,k;
int a,c[N];
bool vis[N];
inline int lowbit(int x){
return x&(-x);
}
inline void add(int x,int val){
while(x<=k) c[x]+=val,x+=lowbit(x);
}
inline int sum(int x){
int ans=0;
while(x>0) ans+=c[x],x-=lowbit(x);
return ans;
}
inline int binarySearch(int num){
int s=1,t=k,mid;
while(s<=t){
mid=(s+t)>>1;
int ret=sum(mid);
if(ret==num && !vis[mid]) return mid;
if(ret<num) s=mid+1;
else t=mid-1;
}
return -1;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&k);
clr(c);clr(vis);
rep(i,1,k+1) add(i,1);
rep(i,0,k){
scanf("%d",&a);a++;
int b=binarySearch(a);
vis[b]=1;
add(b,-1);
printf("%d",b);
if(i<k-1) printf(" ");
else printf("\n");
}
}
return 0;
}