【BZOJ1500】【块状链表】维修数列
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
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
10
1
10
HINT
Source
【分析】
发现大家都是用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 }