NOIP2012 借教室 Splay初探

终于把区间操作的Splay搞明白了……

Splay的大致框架是这样的:

 1 template <class T>
 2 struct SplayNode
 3 {
 4     typedef SplayNode<T> Node;
 5     Node* lch;
 6     Node* rch;
 7     Node* parent;
 8     T val;
 9
10     SplayNode(const T& _val,Node* _parent):
11             lch(0),rch(0),parent(_parent),val(_val) {}
12
13     void passDown() {}
14     void update() {}
15
16     void lRotate()
17     {
18         if(parent->parent)
19         {
20             if(parent==parent->parent->lch)
21                 parent->parent->lch=this;
22             else parent->parent->rch=this;
23         }
24
25         parent->passDown();
26         passDown();
27
28         parent->rch=this->lch;
29         if(lch) lch->parent=this->parent;
30
31         lch=parent;
32         parent=parent->parent;
33         lch->parent=this;
34
35         lch->update();
36         update();
37     }
38
39     void rRotate()
40     {
41         if(parent->parent)
42         {
43             if(parent==parent->parent->lch)
44                 parent->parent->lch=this;
45             else parent->parent->rch=this;
46         }
47
48         parent->passDown();
49         passDown();
50
51         parent->lch=this->rch;
52         if(rch) rch->parent=this->parent;
53
54         rch=parent;
55         parent=parent->parent;
56         rch->parent=this;
57
58         rch->update();
59         update();
60     }
61
62     Node* splay()
63     {
64         while(parent)
65         {
66             int status=0;
67             if(this==parent->lch) status|=1; else status|=2;
68             if(parent->parent)
69             {
70                 if(parent==parent->parent->lch) status|=4;
71                 else status|=8;
72             }
73
74             switch(status)
75             {
76             case 1:  rRotate(); break;
77             case 2:  lRotate(); break;
78             case 5:  parent->rRotate(); this->rRotate(); break;
79             case 6:  lRotate(); rRotate(); break;
80             case 9:  rRotate(); lRotate(); break;
81             case 10: parent->lRotate(); this->lRotate(); break;
82             }
83         }
84         return this;
85     }
86 };

注意双旋的Zig-Zig(Zag-Zag)和Zig-Zag(Zag-Zig),后者可以分解成两次单旋,而前者不能。

借教室一题的75分代码(Vijos):

(Splay果然常数大……当然很可能是我写萎了……)

  1 #include <algorithm>
  2
  3 using std::max;
  4 using std::min;
  5
  6 struct SplayNode
  7 {
  8     typedef SplayNode Node;
  9     Node* lch;
 10     Node* rch;
 11     Node* parent;
 12
 13     int idx;
 14     int val;
 15     int minVal;
 16     int lazyTag;
 17
 18     SplayNode(int _idx,int _val,Node* _parent):
 19             lch(0),rch(0),parent(_parent),idx(_idx),val(_val),
 20             minVal(_val),lazyTag(0) {}
 21
 22     int actual() { return minVal + lazyTag; }
 23
 24     void passDown()
 25     {
 26         if(!lazyTag) return;
 27
 28         if(lch) lch->lazyTag += this->lazyTag;
 29         if(rch) rch->lazyTag += this->lazyTag;
 30
 31         val += lazyTag;
 32         minVal += lazyTag;
 33         lazyTag = 0;
 34     }
 35
 36     void update()
 37     {
 38         minVal = lch ?
 39             ( rch ? min(min(lch->actual(),rch->actual()),this->val) :
 40                     min(lch->actual(),this->val) ) :
 41             ( rch ? min(rch->actual(),this->val) : this->val );
 42     }
 43
 44     void lRotate()
 45     {
 46         if(parent->parent)
 47         {
 48             if(parent==parent->parent->lch)
 49                 parent->parent->lch=this;
 50             else parent->parent->rch=this;
 51         }
 52
 53         parent->passDown();
 54         passDown();
 55
 56         parent->rch=this->lch;
 57         if(lch) lch->parent=this->parent;
 58
 59         lch=parent;
 60         parent=parent->parent;
 61         lch->parent=this;
 62
 63         lch->update();
 64         update();
 65     }
 66
 67     void rRotate()
 68     {
 69         if(parent->parent)
 70         {
 71             if(parent==parent->parent->lch)
 72                 parent->parent->lch=this;
 73             else parent->parent->rch=this;
 74         }
 75
 76         parent->passDown();
 77         passDown();
 78
 79         parent->lch=this->rch;
 80         if(rch) rch->parent=this->parent;
 81
 82         rch=parent;
 83         parent=parent->parent;
 84         rch->parent=this;
 85
 86         rch->update();
 87         update();
 88     }
 89
 90     Node* splay()
 91     {
 92         while(parent)
 93         {
 94             int status=0;
 95             if(this==parent->lch) status|=1; else status|=2;
 96             if(parent->parent)
 97             {
 98                 if(parent==parent->parent->lch) status|=4;
 99                 else status|=8;
100             }
101
102             switch(status)
103             {
104             case 1:  rRotate(); break;
105             case 2:  lRotate(); break;
106             case 5:  parent->rRotate(); this->rRotate(); break;
107             case 6:  lRotate(); rRotate(); break;
108             case 9:  rRotate(); lRotate(); break;
109             case 10: parent->lRotate(); this->lRotate(); break;
110             }
111         }
112         return this;
113     }
114 };
115
116 const int maxN=1e6+5;
117
118 SplayNode* node[maxN];
119 int n,m;
120
121 int change(int d,int s,int t)
122 {
123     int status=0;
124     if(s==1) status |= 1;
125     if(t==n) status |= 2;
126
127     switch(status)
128     {
129     case 0:
130         node[s-1]->splay();
131         node[s-1]->rch->parent=0;
132         node[t+1]->splay();
133         node[s-1]->rch=node[t+1];
134         node[t+1]->parent=node[s-1];
135
136         node[t+1]->lch->lazyTag -= d;
137         node[t+1]->update();
138         node[s-1]->update();
139         return s-1;
140     case 1:
141         node[t+1]->splay();
142         node[t+1]->lch->lazyTag -= d;
143         node[t+1]->update();
144         return t+1;
145     case 2:
146         node[s-1]->splay();
147         node[s-1]->rch->lazyTag -= d;
148         node[s-1]->update();
149         return s-1;
150     case 3:
151         node[1]->splay();
152         node[1]->val -= d;
153         node[1]->minVal -= d;
154         if(node[1]->rch) node[1]->rch->lazyTag -= d;
155         return 1;
156     }
157 }
158
159 #include <cstdarg>
160 #include <cstdio>
161 #include <cctype>
162
163 void readInt(int argCnt,...)
164 {
165     va_list va;
166     va_start(va,argCnt);
167
168     while(argCnt--)
169     {
170         int* dest=va_arg(va,int*);
171         int destVal=0;
172         char curDigit;
173
174         do curDigit=getchar(); while(!isdigit(curDigit));
175         while(isdigit(curDigit))
176         {
177             destVal = destVal * 10 + curDigit - ‘0‘;
178             curDigit=getchar();
179         }
180         *dest=destVal;
181     }
182
183     va_end(va);
184 }
185
186 int main()
187 {
188     readInt(2,&n,&m);
189     int r;
190     readInt(1,&r);
191     node[1]=new SplayNode(1,r,0);
192
193     for(int i=2;i<=n;i++)
194     {
195         readInt(1,&r);
196         node[i]=new SplayNode(i,r,node[i-1]);
197         node[i-1]->rch=node[i];
198     }
199
200     for(int i=1;i<=m;i++)
201     {
202         int d,s,t;
203         readInt(3,&d,&s,&t);
204         int rt=change(d,s,t);
205         if(node[rt]->minVal<0)
206         {
207             printf("-1\n%d",i);
208             return 0;
209         }
210     }
211     printf("0");
212     return 0;
213 }

对于这道题,我们需要给每个Node额外设立3个域:idx(教室的标号,作为键值),minVal(子树中val的最小值),和lazyTag(修改的懒惰标记)

对区间[L,R]进行修改时,首先将L-1提到根,然后将R+1提到根的右孩子处,那么R+1的左孩子就是待修改的区间

当然要特判L==1和R==n(即左/右端为边界的情况)

注意旋转过程中要不断下传lazyTag并对节点的minVal值更新

(这个超级麻烦,一定要把每个细节都想全了,稍有一点疏忽就会出错,而且很不好查)

询问时直接询问根节点的minVal值即可(注意要让根节点的lazyTag传下去)

时间: 2024-10-29 19:07:52

NOIP2012 借教室 Splay初探的相关文章

NOIP2012借教室[线段树|离线 差分 二分答案]

题目描述 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要 向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们自然希望编程解决这个问题. 我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借.共有m份 订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租 借教室(包括第sj天和第tj天),每天需要租借dj个教室. 我们假定,租借者对教室的大小.地点没

浅谈差分数组的应用(二)&amp;[NOIP2012]借教室题解

[NOIP2012提高&洛谷P1083]借教室 Description 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样.面对海量租借教室的信息,我们自然希望编程解决这个问题. 我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借.共有m份订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租借教室(包括第sj天和第tj天),每天需要

noip2012借教室

Description Input Output Sample Input 4 3 2 5 4 3 2 1 3 3 2 4 4 2 4 Sample Output -1 2 Data Constraint Hint 第一份订单满足后,4天剩余教室分数分别为0,3,2,3. 第二份订单要求第二天到第四天每天提供三个教室,而第三天剩余的教室数为二,因此无法满足.分配停止通知第二个申请人修改订单. 10% -> 1<=n,m<=10 30% -> 1<=n,m<=1000 7

NOIP2012 借教室

自己YY出来的方法,时间复杂度为n*m,但实际上并没有这么多,codeVS上最慢的一组有300多ms. 对于每一个订单,在它的左端点加上需要的教室数量,在右端点加1的位置减去需要的教室数量.然后统计前缀和,如果有某个点的前缀和大于了当天教室的数量,那么就有的订单就有问题了,然后从订单编号从小到大统计在订单区间的教室数量,保存恰好不能满足的那个订单:对于之后不满足题意的前缀和,统计订单的最大编号不能超过之前保存的订单标号. #include<cstdio> #define MAXN 100000

[NOIP2012]借教室 题解

题目大意: 有一个n个数的数列,m个操作,第i个操作使[li,ri]区间建di,问第几个操作使数列中出现负数. 思路: 暴力显然过不了,那么就可以优化了,不难想到线段树,显然需要良好的姿势,那么就差分. a[i]表示第i天比第i-1天多了多少房间,于是a的前缀和即为该天的房间数量.而a的维护显然为a[li]+=di,a[ri+1]-=di. 因为求最前的操作,于是我们可以二分答案.但如此常数比较大,又有冗余,可以来个栈一样的东西节省时间. 但是有大神想到了O(n+m)的算法.假设m个指令都可满足

【差分序列】【NOIP2012】借教室

1266. [NOIP2012] 借教室 ★★☆ 输入文件:classrooms.in 输出文件:classrooms.out 简单对比 时间限制:1 s 内存限制:128 MB [题目描述] 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要 向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们自然希望编程解决这个问题. 我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借.共有m份 订单,每

【NOIP2012】借教室

在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要 向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们自然希望编程解决这个问题. 我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借.共有m份 订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租 借教室(包括第sj天和第tj天),每天需要租借dj个教室. 我们假定,租借者对教室的大小.地点没有要求.即

NOIp2012:借教室

题目描述 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们自然希望编程解决这个问题. 我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借.共有m份订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租借教室(包括第sj天和第tj天),每天需要租借dj个教室. 我们假定,租借者对教室的大小.地点没有要求

NOIP2012提高组 Day 2 Problem 2 借教室

原题 题目描述 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们自然希望编程解决这个问题. 我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借.共有m份订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租借教室(包括第sj天和第tj天),每天需要租借dj个教室. 我们假定,租借者对教室的大小.地点没