【BZOJ1500】【块状链表】SuperMemo

Description

Input

输入文件的第1行包含两个数N和M,N表示初始时数列中数的个数,M表示要进行的操作数目。第2行包含N个数字,描述初始时的数列。以下M行,每行一条命令,格式参见问题描述中的表格。

Output

对于输入数据中的GET-SUM和MAX-SUM操作,向输出文件依次打印结果,每个答案(数字)占一行。

Sample Input

9 8
2 -6 3 5 1 -5 -3 6 3
GET-SUM 5 4
MAX-SUM
INSERT 8 3 -5 7 2
DELETE 12 1
MAKE-SAME 3 3 2
REVERSE 3 6
GET-SUM 5 4
MAX-SUM

Sample Output

-1
10
1
10

HINT

Source

Splay

【分析】

发现大家都是用splay做的,觉得用块状链表好像也可以做,结果真的可以....,捣鼓了一个下午,终于弄出来了.....

其实想好了还是很简单的(那你还做了一个下午,太弱了)。

用论文上的方法进行删除和添加数值,虽然效率有点低下,但是对付这道题还是够了。

因为没有牵涉到合并,因此不会涉及到标记的合并等等。

其实牵涉到了也没关系.....update一下就可以暴力合并,复杂度不会上升。

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <algorithm>
  4 #include <cstring>
  5 #include <vector>
  6 #include <utility>
  7 #include <iomanip>
  8 #include <string>
  9 #include <cmath>
 10 #include <queue>
 11 #include <assert.h>
 12 #include <map>
 13 #include <ctime>
 14 #include <cstdlib>
 15
 16 const int MAXN = 500001;
 17 const int INF = 10000000;
 18 const int SIZE = 1200;
 19 using namespace std;
 20 struct Node{
 21        int shu[SIZE+2];
 22        int size, sum, samen;
 23        int lmax, rmax, mmax;
 24        Node *l, *r;
 25        bool turn, same;
 26
 27        Node(){//构造函数
 28              size = sum = samen = same = 0;
 29              lmax = rmax = mmax = -INF;
 30              l = r = NULL;
 31              turn = 1;
 32        }
 33        //对于两个较小的数列合并也要update
 34        void update(){
 35             if (same){//是否改为同样的
 36                for (int i = 1; i <= size; i++) shu[i] = samen;
 37                turn = 1;same = 0;
 38                return;
 39             }
 40             if (!turn){//反转序列
 41                for (int i = 1; i <= (size>>1);i++) swap(shu[i], shu[size - i + 1]);
 42                swap(lmax, rmax);
 43                turn = 1;
 44             }
 45        }
 46        void Count(){
 47             update();
 48             int t = 0;
 49             sum = 0;
 50             rmax = lmax = mmax = -INF;
 51             //序列中间的一段和
 52             for (int i = 1; i <= size; i++){
 53                 sum += shu[i];
 54                 t = max(t + shu[i], shu[i]);
 55                 mmax = max(t, mmax);
 56             }
 57             t = 0;
 58             for (int i = 1; i <= size; i++) {t += shu[i]; lmax = max(lmax, t);}t = 0;
 59             for (int i = size; i >= 1; i--) {t += shu[i]; rmax = max(rmax, t);}
 60        }
 61 };
 62 //块状链表
 63 struct Block{
 64        Node *head, *p;
 65        int dir;
 66
 67        Block(){head = new Node;}
 68        void find(int x){//移动到x所在的块,顺便压缩路径
 69             int tmp = 0;
 70             p = head;
 71             while (p->size + tmp < x){
 72                   tmp += p->size;
 73                   if (p->size == 0){
 74                      p = p->l;
 75                      p->r = p->r->r;
 76                      if (p->r != NULL) p->r->l = p;
 77                   }
 78                   p = p->r;
 79             }
 80             dir = x - tmp;
 81        }
 82        //将a分成a和b两个块
 83        Node *NEW(Node *&a){
 84             Node *b = new Node;
 85             b->r = a->r;
 86             b->l = a;
 87             if (a->r != NULL) a->r->l = b;
 88             a->r = b;
 89             return b;
 90        }
 91        void Insert(int pos,int size,int *data){ //插入操作
 92             Node *p2;
 93             find(pos);
 94             p->update();
 95             //如果指向中间,就分裂,注意这一段在开头也同样可以操作!
 96             if (dir != p->size){
 97                //这种操作与普通的块状链表有点不一样,但是更加便于理解
 98                p2 = NEW(p);
 99                memcpy(&p2->shu[1], &p->shu[dir + 1], sizeof(int) * (p->size - dir));
100
101                p2->size = p->size - dir;
102                p->size = dir;
103
104                p2->Count();
105                p->Count();
106             }
107             int i = 1;//插入数列
108             while (i <= size){
109                   int tmp = min(1200 - p->size, size - i + 1);
110                   memcpy(&p->shu[p->size + 1], &data[i], sizeof(int)*tmp);
111                   i += tmp;
112                   p->size += tmp;
113                   if (size >= i){//创建新的列表再加入
114                      p->Count();
115                      p2 = NEW(p);
116                      p = p2;
117                   }
118             }
119             p->Count();
120        }
121        void Delete(int pos,int num){
122             Node *p2;
123             find(pos);
124             while (num > 0){
125                   if ((dir == 1) && (num >= p->size)){
126                      //删除一个块
127                      num -= p->size;
128                      if (p->l != NULL) p->l->r = p->r;
129                      else head = p->r;//不然就是表头
130                      if (p->r != NULL) p->r->l = p->l;
131                      p2 = p;
132                      p = p->r;
133                      delete p2;
134                   }else{//暴力删除直到一个块
135                      p->update();
136                      //tmp记录能删除的最远位置
137                      int tmp = min(dir + num - 1, p->size);
138                      num -= tmp - dir + 1;
139                      for (int i = 1; i <= p->size - tmp; i++) p->shu[dir + i - 1] = p->shu[tmp + i];
140                      p->size -= tmp - dir + 1;
141                      p->Count();
142                      p = p->r;
143                      dir = 1;
144                   }
145             }
146        }
147        //反转操作
148        void Reverse(int pos, int num){
149             Node *ap,*bp,*cp,*dp;
150             Node *p2;
151             bool first=true;
152             int tmp;
153             find(pos);
154
155             if (p->size >= dir + num - 1){//直接暴力
156                p->update();
157                for (int i = 1; i <= (num >> 1); i++) swap(p->shu[dir + num - i], p->shu[dir + i - 1]);
158                p->Count();
159                return;
160             }
161             if (dir > 1){//拆第一块
162                p->update();
163                num -= p->size - dir + 1;
164                p2 = NEW(p);
165                memcpy(&p2->shu[1], &p->shu[dir], sizeof(int)*(p->size-dir+1));
166                p2->size = p->size - dir + 1;
167                p->size = dir - 1;
168
169                ap = p2;
170                cp = p;
171                p2->Count();
172                p->Count();
173                p = p2->r;
174             }else{
175                ap = p;
176                cp = ap->l;
177             }
178             while (num > p->size){
179                   num -= p->size;
180                   p = p->r;
181             }
182             //最后一块也要拆
183             if (num != p->size){
184                p->update();
185                p2 = NEW(p);
186                memcpy(&p2->shu[1],&p->shu[num+1],sizeof(int)*(p->size-num));
187                p2->size = p->size - num;
188                p->size = num;
189
190                bp = p;
191                dp = p2;
192                p2->Count();
193                p->Count();
194             }else{
195                //简单的反转一下
196                bp = p;
197                dp = p->r;
198             }
199             //从开头开始大反转
200             p = ap;
201             while (1){
202                   swap(p->l, p->r);
203                   p->turn = !p->turn;
204                   swap(p->lmax, p->rmax);
205                   if (p == bp) break;
206                   p = p->l;//注意因为已经换了所以是向左走
207             }
208             //将整个块倒转
209             if (cp != NULL) cp->r = bp;
210             else head = bp;
211             ap->r = dp;
212             bp->l = cp;
213             if (dp != NULL) {dp->l = ap;}
214        }
215        //使一段序列变成相同的值
216        void MakeSame(int pos,int num,int val){
217             find(pos);
218             while (num > 0){
219                   if  ((dir == 1) && (num >= p->size)){
220                       //整块操作
221                       p->same = 1;
222                       p->samen = val;
223                       p->sum = p->size * val;
224                       p->mmax = max(val, p->sum);//注意因为val可能是负数,所以要这样写
225                       p->rmax = p->lmax = p->mmax;
226                       num -= p->size;
227                       p = p->r;
228                   }else{
229                       p->update();//暴力操作
230                       int tmp = min(dir + num - 1, p->size);
231                       for (int i = dir; i <= tmp; i++) p->shu[i] = val;
232                       p->Count();
233                       num -= tmp - dir + 1;
234                       p = p->r;
235                       dir = 1;
236                   }
237             }
238        }
239        //区间和
240        int GetSum(int pos,int x){
241            int tmp = 0;
242            find(pos);
243            while (x > 0){
244                  if ((dir == 1) && (x >= p->size)){
245                     tmp += p->sum;
246                     x -= p->size;
247                     p = p->r;
248                  }else{ //暴力
249                     p->Count();
250                     int t = min(dir + x - 1, p->size);
251                     for (int i = dir; i <= t; i++) tmp += p->shu[i];
252                     x -= t - dir + 1;
253                     p = p->r;
254                     dir = 1;
255                  }
256            }
257            return tmp;
258        }
259        //区间最大值
260        int MaxSum(){
261            int Ans = -INF, last = -INF;
262            p = head;
263            while (p != NULL){
264                  if (p->size != 0){
265                     int tmp;
266                     tmp = max(last + p->sum, max(p->rmax, p->sum));
267                     Ans = max(Ans, max(last + p->lmax, max(tmp, p->mmax)));
268                     last = tmp;
269                  }
270                  p = p->r;
271            }
272            return Ans;
273        }
274 }A;
275
276 int m, pos;
277 int n,tmp, data[MAXN];
278 char str[20];
279
280 void init(){
281     scanf("%d%d", &n, &m);
282     for (int i = 1; i <= n; i++) scanf("%d", &data[i]);
283     tmp = 0;
284     A.Insert(tmp, n, data);
285 }
286 void work(){
287     for (int i = 1; i <= m; i++){
288         scanf("%s", str);
289         if (str[2] != ‘X‘) scanf("%d%d", &pos, &n);
290         if (str[2] == ‘S‘){
291            for (int i = 1; i <= n; i++) scanf("%d", &data[i]);
292            A.Insert(pos, n, data);
293         }else if (str[2] == ‘L‘) A.Delete(pos, n);
294         else if (str[2] == ‘K‘) {
295              int tmp;
296              scanf("%d", &tmp);
297              A.MakeSame(pos, n, tmp);
298         }else if (str[2] == ‘T‘) printf("%d\n", A.GetSum(pos, n));
299         else if (str[2] == ‘V‘) A.Reverse(pos,n);
300         else if (str[2] == ‘X‘) printf("%d\n", A.MaxSum());
301     }
302 }
303
304 int main(){
305     #ifdef LOCAL
306     freopen("data.txt", "r", stdin);
307     freopen("out.txt", "w", stdout);
308     #endif
309     init();
310     work();
311     return 0;
312 }

时间: 2024-12-27 13:19:39

【BZOJ1500】【块状链表】SuperMemo的相关文章

【BZOJ2741】【块状链表+可持久化trie】FOTILE模拟赛L

Description FOTILE得到了一个长为N的序列A,为了拯救地球,他希望知道某些区间内的最大的连续XOR和. 即对于一个询问,你需要求出max(Ai xor Ai+1 xor Ai+2 ... xor Aj),其中l<=i<=j<=r. 为了体现在线操作,对于一个询问(x,y): l = min ( ((x+lastans) mod N)+1 , ((y+lastans) mod N)+1 ).r = max ( ((x+lastans) mod N)+1 , ((y+last

【POJ2887】【块状链表】Big String

Description You are given a string and supposed to do some string manipulations. Input The first line of the input contains the initial string. You can assume that it is non-empty and its length does not exceed 1,000,000. The second line contains the

c++之路进阶——块状链表(弹飞绵羊)

2333 弹飞绵羊 2010年省队选拔赛湖南 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 大师 Master 题解 题目描述 Description Lostmonkey发明了一种超级反弹装置.为了在绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏.游戏一开始,Lostmonkey在地上沿一条直线摆放 n个反弹装置,并按从前往后的方式将反弹装置依次编号为 0 到 n-1,对 0≤i≤n-1,为第 i 个反弹装置设定了初始弹力系数 ki,当绵羊落到第 i 个反弹装置上时,它将被往后

c++之路——块状链表(教主的魔法)

F.A.Qs Home Discuss ProblemSet Status Ranklist Contest ModifyUser  gryz2016 Logout 捐赠本站 Notice:由于本OJ建立在Linux平台下,而许多题的数据在Windows下制作,请注意输入.输出语句及数据类型及范围,避免无谓的RE出现. 3343: 教主的魔法 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 711  Solved: 309[Submit][Stat

hdu 1754 块状链表 单点修改+单点查询

经典的线段树题目,也可以用块状链表做. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <cmath> 5 using namespace std; 6 7 const int N = 200000; 8 const int M = 800; 9 int n, m, tot; 10 11 int max( int a, int b ) 12 { 13 retu

poj 2887 块状链表

块状链表第一题,懒得写成链表形式的,所以写成了静态链表. 思想还是很简单的,就是进行分块查找和插入,和建立索引有点像,复杂度是根号的,实现起来比较容易,由于这个题插入操作不多,所以没有写split函数, 因为没有删除操作,所以也没有写union函数,随后有空再补上吧. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <cmath> 5 using namesp

【块状链表】AutSky_JadeK的块状链表模板+总结(STL版)

Part 1.块状链表.   定位 插入 删除 数组 O(1) O(n) O(n) 链表 O(n) O(1) O(1) 对于线性表的以上常见操作来说,数组和链表都无法有效地解决.但是,若我们将链表的每个节点存成一个数组,使得链表里每个节点的数据拼接起来就是原先的线性表中的内容(即块状链表),并且数组的大小合适的话,以上的操作都能比较好地解决了.根据均值不等式,若每个块的大小定为sqrt(n)左右最优,此时块数也是sqrt(n)左右,易证.以下是块状链表的基础操作的思想.复杂度和代码. 一.声明.

块状链表 codevs 2333弹飞绵羊

块状链表,分块处理,先预处理每一个点跳到下一个块 跳到哪,步数.然后修改的时候,修该那一个块即可 #include<cstdio>#include<cmath>int a[200006],n,m,b[200006],c[200006],dian[200006],l[200006],r[200006];int main(){ scanf("%d",&n); for(int i=1;i<=n;i++)   scanf("%d",&a

【HDU4391】【块状链表】Paint The Wall

Problem Description As a amateur artist, Xenocide loves painting the wall. The wall can be considered as a line consisting of n nodes. Each node has its own color. Xenocide spends all day in front of the wall. Sometimes, he paints some consecutive no