题目意思是每个人来到之后就会直接插入到pos处的位置,问最终整条队伍的顺序。
刚开始的时候理解错题意,一直wa,等理解正确后才发现这道题考得很巧妙,真正理解了树状数组和线段树的人才可能做得出来。这里是用树状数组来解决的。大家都知道树状数组的c[]所保存的数据有利于快速的求和从而得到某区间的和或者是处于某一点处数据的排名。这里正是利用第二点作为突破口。
逆序地求出每一个元素的pos在树状数组的结点位置,即为该元素在队列中的最终位置,而从1到该位置处的区间元素总和就等与pos+1(pos不能为0),因此要配合使用结点元素减1的操作。
讲得可能还是不好,看代码吧:
- #include<cstdio>
#include<iostream>
using namespace std; - const int N=200100;
int c[N];
int pos[N],val[N],ans[N];
int n; - int lowbit(int n)
- {
return n&(-n);
}
- void del(int p)
{
while(p<=N)
{
c[p]--;
p+=lowbit(p);
}
} - int getK(int k)
{
int ans=0,cnt=0;
for(int i=18;i>=0;i--)
{
ans+=(1<<i);
if(ans>=n || cnt+c[ans]>=k) ans-=(1<<i);
else cnt+=c[ans];
}
return ans+1;
} - int main()
{
while(scanf("%d",&n)==1)
{
int i;
for(i=1;i<=n;i++)
{
scanf("%d%d",&pos[i],&val[i]);
c[i]=lowbit(i);
}
for(i=n;i;i--)
{
int ps=getK(pos[i]+1);
ans[ps]=val[i];
del(ps);
}
for(i=1;i<=n;i++)
printf("%d ",ans[i]);
printf("/n");
}
return 0;
}
这题也可以用线段树来求解,思想没变,其实这题和poj 2182是一样的:
- #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int M=400000;
struct Tree
{
int l,r;
int cnt;
}T[3*M];
struct peo
{
int num;
int val;
bool operator <(const peo a)const{
return num<a.num;
}
}pe[M/2+100];
int n,N; - void build(int l,int r,int p)
{
T[p].l=l;
T[p].r=r;
T[p].cnt=r+1-l;
if(l==r) return ;
int mid=(l+r)>>1;
build(l,mid,p<<1);
build(mid+1,r,p<<1|1);
} - void del(int c,int p)
{
if(T[p].l==T[p].r)
{
pe[n].num=T[p].l;
T[p].cnt=0;
return ;
}
if(c<=T[p<<1].cnt)
del(c,p<<1);
else
del(c-T[p<<1].cnt,p<<1|1);
T[p].cnt--;
} - int main()
{
while(scanf("%d",&N)==1)
{
int i;
for(i=1;i<=N;i++)
scanf("%d%d",&pe[i].num,&pe[i].val);
build(1,N,1);
for(n=N;n>0;n--)
{
del(pe[n].num+1,1);
}
sort(pe+1,pe+N+1);
for(i=1;i<=N;i++)
printf("%d ",pe[i].val);
printf("/n");
}
return 0;
}