POJ 2828 - Buy Tickets(线段树)

题目链接 https://cn.vjudge.net/problem/POJ-2828

【题意】
有N个人排名买票,现在给出每个人要插入的位置pos(0<=pos<=N-1)以及他的价值val. 在插入N个人后,会构成一个新序列. 现在让你按顺序输出新序列中每个位置pos对应的价值val

【思路】
首先我们知道——最后一个人一定会得到当前队伍他想要的位置,我们可以选择逆过程插入,这样的话,对于插入在pos位置的人,他的前面一定要有pos个空位(这里0<=pos<=N-1),因此,我们只需要找出前面有pos个空位的节点插入,然后将该节点的空位数清0并维护其它区间的空位数.
线段树维护区间剩余空位数(区间和).每次插入时,若左儿子的sum值大于pos,那么只需要在左儿子找, 否则在右儿子找,但是注意pos = pos - 左儿子的lson.sum(因为左儿子已经有lson.sum个空位,我们只需要再找pos - lson.sum个)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define node tree[id]
#define lson tree[id<<1]
#define rson tree[id<<1|1]
using namespace std;

const int maxn=200005;

struct Tree{
	int left,right;
	int sum;
}tree[maxn<<2];

int n;
int p[maxn],v[maxn];
int a[maxn];

void pushup(int id){node.sum=lson.sum+rson.sum;}

void build(int id,int le,int ri){
	node.left=le;
	node.right=ri;
	if(le==ri){
		node.sum=1;
		return;
	}
	int mid=(le+ri)>>1;
	build(id<<1,le,mid);
	build(id<<1|1,mid+1,ri);
	pushup(id);
}

int query(int id,int val){//查询前缀和sum[1,pos]==val时的pos
	if(node.left==node.right){
		return node.left;
	}
	if(lson.sum>=val) return query(id<<1,val);
	else return query(id<<1|1,val-lson.sum);
}

void update(int id,int pos){
	if(node.left==node.right){
		node.sum=0;
		return;
	}
	int mid=(node.left+node.right)>>1;
	if(pos<=mid) update(id<<1,pos);
	else update(id<<1|1,pos);
	pushup(id);
}

int main(){
	while(scanf("%d",&n)==1){
		for(int i=1;i<=n;++i){
			scanf("%d%d",&p[i],&v[i]);
			++p[i];
		}
		build(1,1,n);
		for(int i=n;i>=1;--i){
			int pos=query(1,p[i]);
			a[pos]=v[i];
			update(1,pos);
		}
		for(int i=1;i<=n;++i) printf("%d%c",a[i],i==n?'\n':' ');
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xiao_k666/article/details/82837703