题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4803
题目大意:给你一段连续的区间,现在向其中插入数,每个位置只能插一次,每次从初始位置开始找第k个空位置,问第k个空位置的下标
解题思路:我们现在用1表示该位置为空,则初始状态每个座位都是空的,将1插入树状数组1-n中,每次寻找第k个不为空的位置时,用二分求和查找前x项的和为k-1,则第k个空位置为x+1,然后把该位置标记为0,即在该位置插入-1,代码如下:
#include<stdio.h>
#include<string.h>
#define N 50005
int n,m;
int c[N],ans[N];
int lowbit(int x) {return x&(-x);}
void insert(int x,int v)
{
while(x<=n)
{
c[x]+=v;
x+=lowbit(x);
}
}
int get_sum(int x)
{
int sum=0;
while(x>0)
{
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
int main()
{
int a,q;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)//初始化每个空位置为1
insert(i,1);
for(int i=1;i<=n;i++)
{
scanf("%d",&a);
int l=1,r=n,mid;
while(l<=r)//二分查找第k个空位置
{
mid=(l+r)>>1;
if(get_sum(mid)>=a)
r=mid-1;
else
l=mid+1;
}
ans[i]=l;//存入结果数组
insert(l,-1);将改位置标记为已占
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d",&q);
printf("%d%c",ans[q],i==m?'\n':' ');
}
}
}
当然,只要能二分,一般都能用下面的函数:代码如下:
#include<stdio.h>
#include<string.h>
#define N 50005
int n,m;
int c[N],ans[N];
int lowbit(int x) {return x&(-x);}
void insert(int x,int v)
{
while(x<N)
{
c[x]+=v;
x+=lowbit(x);
}
}
int get_sum(int x)
{
int sum=0;
while(x>0)
{
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
int find_kth(int k)
{
int ans=0,cnt=0;
for(int i=20;i>=0;i--)
{
ans+=(1<<i);
if(ans>n||cnt+c[ans]>=k)//此处需注意:如果上面的insert()里面是x<N的话,ans>n或者ans>=N都行,如果上面的
//insert()里面是x<=n的话,这就必须写为ans>n,因为插入更新的n,导致c数组n后面都为零;
ans-=(1<<i);
else
cnt+=c[ans];
//printf("----->%d %d\n",cnt,ans);
}
return ans+1;
}
int main()
{
int a,q;
while(~scanf("%d",&n))
{
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)
insert(i,1);
for(int i=1;i<=n;i++)
{
scanf("%d",&a);
ans[i]=find_kth(a);
//printf("---->%d\n",ans[i]);
insert(ans[i],-1);
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d",&q);
printf("%d%c",ans[q],i==m?'\n':' ');
}
}
}