Buy Tickets POJ
阿新 • • 發佈:2018-12-16
題意
有N個人排名買票,現在給出每個人要插入的位置pos(0<=pos<=N-1)以及他的價值val。在插入N個人後,會構成一個新序列。現在讓你按順序 輸出新序列中每個位置pos對應的價值val
思路
最後一次插入某個位置的人,他的位置不會再改變,所以我們應該從後往前插入。用sum記錄所在空間剩餘的位置。對於插入在pos位置的人他前邊一定要有pos-1(1~n 建樹)個剩餘位置。(0~n 建樹則為 pos個剩餘位置)
訪問結點時,如果左子樹的剩餘位置大於或等於 pos 就訪問左子樹(sum[rt<<1] >= pos),否則就訪問右子樹。訪問右子樹時需要用pos減去左子樹的剩餘位置( pos-sum[rt<<1] ),即整個線段樹的第pos個空位(空葉子結點),也是在右兒子那的第pos-sum[rt<<1]個空位。
#include<cstdio> const int maxn=200000; int sum[maxn<<2],pos[maxn<<2],val[maxn<<2],ans[maxn<<2]; //ans儲存插入的值 void pushup(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void bulid(int l,int r,int rt) { if(l==r) { sum[rt]=1; //初始化每個葉子結點剩餘位置為 1 return ; } int m=(l+r)>>1; bulid(l,m,rt<<1); bulid(m+1,r,rt<<1|1); pushup(rt); } void update(int p,int v,int l,int r,int rt) { if(l==r) { ans[rt]=v; sum[rt]--; return ; } int m=(l+r)>>1; if(sum[rt<<1]>=p) //判斷插入左子樹還是右子樹 update(p,v,l,m,rt<<1); else update(p-sum[rt<<1],v,m+1,r,rt<<1|1); pushup(rt); } void print(int l,int r,int rt) { if(l==r) { printf("%d ",ans[rt]); return ; } int m=(l+r)>>1; print(l,m,rt<<1); print(m+1,r,rt<<1|1); } int main() { int n; while(~scanf("%d",&n)) { bulid(1,n,1); for(int i=0;i<n;i++) scanf("%d%d",&pos[i],&val[i]); for(int i=n-1;i>=0;i--) update(pos[i]+1,val[i],1,n,1); print(1,n,1); printf("\n"); } return 0; }
參考部落格