poj2828(线段树查找序列第k小的值)
作者:互联网
题目链接:https://vjudge.net/problem/POJ-2828
题意:有n个人,依次给出这n个人进入队列时前面有多少人p[i],和它的权值v[i],求最终队列的权值序列。
思路:基本类似于poj2182,简化题意后即为求序列1..n中第k小的值的问题。读入数据量比较大,最好读入优化。我们从n..1逆序遍历,则可以确认最后一个人的最终位置为p[n]+1,然后从序列中删除p[n]+1,继续操作倒数第二个...这一操作可以通过线段树来完成。线段树的结点包括3个值:l(区间左端点),r(区间右端点),len(区间剩余编号数)。每次询问维护len值。时间复杂度为O(Tnlogn)。
AC代码:
#include<cstdio> #include<cctype> using namespace std; const int maxn=200005; inline int read(){ int x=0,f=0;char c=0; while(!isdigit(c)) {f|=c=='-';c=getchar();} while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar(); return f?-x:x; } struct node{ int l,r,len; }tr[4*maxn]; int p[maxn],v[maxn],n,ans[maxn]; void build(int v,int l,int r){ tr[v].l=l,tr[v].r=r,tr[v].len=r-l+1; if(l==r) return; int mid=(l+r)>>1; build(2*v,l,mid); build(2*v+1,mid+1,r); } int query(int v,int k){ --tr[v].len; if(tr[v].l==tr[v].r) return tr[v].l; if(k<=tr[2*v].len) return query(2*v,k); else return query(2*v+1,k-tr[2*v].len); } int main(){ while(~scanf("%d",&n)){ build(1,1,n); for(int i=1;i<=n;++i) p[i]=read(),v[i]=read(); for(int i=n;i>=1;--i) ans[query(1,p[i]+1)]=v[i]; for(int i=1;i<=n;++i) printf("%d ",ans[i]); printf("\n"); } return 0; }
标签:题意,int,线段,tr,len,poj2828,查找,序列 来源: https://www.cnblogs.com/FrankChen831X/p/10778996.html