POJ2828:Buy Tickets——题解

http://poj.org/problem?id=2828

首先发现如果我们按照他的方法模拟的话,势必时间爆炸。

所以我们从后往前推,因为我们知道最后一个的位置一定是对的,而前面的位置可以从后面推知。

这样做的好处就是只移动一个元素而不是移动该元素往后的所有元素。

那么怎么查询他应该在第几位就是个问题了。

我们知道线段树怎么写吧。

但是我们不知道线段树的其他功能还有替代部分二叉搜索树的功能。

一个节点表示它的区间内含有的没被插入的位置有多少。

那么我们知道对于一个pos,如果pos小于当前节点的左节点,那么我们往左走,否则减去左节点的值,走右节点。

那么我们就做完了!

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
inline int read(){
    int X=0,w=0; char ch=0;
    while(ch<'0' || ch>'9') {w|=ch=='-';ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
int tree[800001],pos[200001],val[200001],ans[200001];
void build(int a,int l,int r){
    if(l==r){
    tree[a]=1;
    return;
    }
    int mid=(l+r)>>1;
    build(a*2,l,mid);
    build(a*2+1,mid+1,r);
    tree[a]=tree[a*2]+tree[a*2+1];
    return;
}
void gai(int a,int l,int r,int p,int v){
    tree[a]--;
    if(l==r){
    ans[l]=v;
    return;
    }
    int mid=(l+r)>>1;
    if(tree[a*2]>p)gai(a*2,l,mid,p,v);
    else gai(a*2+1,mid+1,r,p-tree[a*2],v);
    return;
}
int main(){
    int n;
    while(scanf("%d",&n)!=EOF){
    build(1,0,n);
    for(int i=1;i<=n;i++){
        pos[i]=read();
        val[i]=read();
    }
    for(int i=n;i>=1;i--){
        gai(1,0,n,pos[i],val[i]);
    }
    for(int i=0;i<n;i++){
        printf("%d ",ans[i]);
    }
    printf("
");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/luyouqi233/p/7866632.html