线段树专辑 —— pku 2828 Buy Tickets

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

灰常有趣的一道逆序思维题目,并且题目掐的很死,用链表什么的,统统tle

如何逆序思维?假设数据如下:

4
0 77
1 51
1 33
2 69

我们从后向前解题,开始是第四个人,他的pos==2,可是他前面分明还有3个人,也就是说,他必须插一个人的队才有可能达到他要达到的位置

再看第三个人,他的pos==1,他前面还有两人,他也需要插一个队

再看第二个人,他的pos==1,前面也只有一个人,1-1==0,他不需要插队

第一个人,0-0==0,也不需要插队了

最终队序是:

77 33 69 51

我们用线段树加快插队速度,如何做?很简单,定义一个left表示该区间剩余的容量,假如要插入第w个位置,那么我们便从tree[1]开始向下查找,如果左子区间的left>=w,那么便向左区间递归下去,否则则向右子区间递归查找w-tree[2*i].left位置。如此这般,直到找到了叶子节点。

这里要注意一下顺序,第一个叶子节点表示的意思是插0个队的位置,第二个叶子节点表示的是插一个队的位置。。。。所以最后输出的时候记得倒过来输出

 

View Code
 1 #include<iostream>
2 #include<string>
3 using namespace std;
4
5 struct node
6 {
7 int l;
8 int r;
9 int left;
10 };
11
12 node tree[1000000];
13
14 struct man
15 {
16 int pos;
17 int val;
18 };
19
20 man num[200001];
21 int n;
22 int ans[200001];
23
24 void build(int i,int l,int r)
25 {
26 tree[i].l=l;
27 tree[i].r=r;
28 tree[i].left=r-l+1;
29 if(l==r)
30 return;
31 int mid=(l+r)/2;
32 build(2*i,l,mid);
33 build(2*i+1,mid+1,r);
34 }
35
36 void updata(int i,int w,int val)
37 {
38 if(tree[i].left>=w)
39 {
40 if(tree[i].l==tree[i].r)
41 {
42 tree[i].left--;
43 ans[tree[i].l]=val;
44 return;
45 }
46 if(tree[i].l<tree[i].r)
47 {
48 if(tree[2*i].left>=w)
49 updata(2*i,w,val);
50 else if(tree[2*i+1].left>=w-tree[2*i].left)
51 updata(2*i+1,w-tree[2*i].left,val);
52 tree[i].left=tree[2*i].left+tree[2*i+1].left;
53 }
54 }
55 }
56
57 int main()
58 {
59 int i,pos;
60 freopen("in.txt","r",stdin);
61 while(scanf("%d",&n)!=EOF)
62 {
63 build(1,1,n);
64 for(i=0;i<n;i++)
65 {
66 scanf("%d%d",&num[i].pos,&num[i].val);
67 }
68 for(i=n-1;i>=0;i--)
69 {
70 pos=i-num[i].pos+1; //i-num[i].pos表示要插队的数目,插x个队,最终的位置便是x+1,所以+1
71 updata(1,pos,num[i].val);
72 }
73 int flag=0;
74 for(i=n;i>=1;i--)
75 {
76 if(flag)
77 printf("");
78 printf("%d",ans[i]);
79 flag=1;
80 }
81 printf("\n");
82 }
83 return 0;
84 }



posted @ 2011-11-14 11:14  Accept  阅读(214)  评论(0编辑  收藏  举报