【无旋 treap】例题
【bzoj3223】文艺平衡树
Description
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1
Input
第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n) m表示翻转操作次数
接下来m行每行两个数[l,r] 数据保证 1<=l<=r<=n
Output
输出一行n个数字,表示原始序列经过m次变换后的结果
Sample Input
1 3
1 3
1 4
Sample Output
HINT
N,M<=100000
【题目分析】
模板题。维护rev标记。
下放标记的地方:$Merge(), Split()$
【CODE】
#include<iostream> #include<cstdlib> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; const int N = 1e5; int n, m; struct node{ node *lc, *rc; bool reverse; int value, pri, size; node(){} node(const int &_v) : value(_v), lc(NULL), rc(NULL), pri(rand()), reverse(false) {} inline node* update(){ size = (lc ? lc->size : 0) + (rc ? rc->size : 0) + 1; return this; } }tree[N], *root = NULL; inline void pushDown(node *u){ if(u->reverse){ if(u->lc) u->lc->reverse ^= 1; if(u->rc) u->rc->reverse ^= 1; swap(u->lc, u->rc); u->reverse = false; } } inline node* Merge(node *u, node *v){ if(!u) return v; if(!v) return u; pushDown(u), pushDown(v); if(u->pri < v->pri){ u->rc = Merge(u->rc, v); return u->update(); } else{ v->lc = Merge(u, v->lc); return v->update(); } } #define sz(u) (u? u->size : 0) inline void Split(node *u, const int &Kth, node *&L, node *&R){ if(!u) return (void)(L = R = NULL); pushDown(u); if(sz(u->lc) < Kth){ Split(u->rc, Kth - sz(u->lc) - 1, L, R); u->rc = NULL, u->update(); L = Merge(u, L); } else{ Split(u->lc, Kth, L, R); u->lc = NULL, u->update(); R = Merge(R, u); } } inline void Split2(node *u, const int &v, node *&L, node *&R){ if(!u) return (void)(L = R = NULL); pushDown(u); if(u->value < v){ Split2(u->rc, v, L, R); u->rc = NULL, u->update(); L = Merge(u, L); } else{ Split2(u->lc, v, L, R); u->lc = NULL, u->update(); R = Merge(R, u); } } inline node* Insert(node *u, int v){ node *L, *R; Split2(u, v, L, R); node *ret = new node(v); return Merge(Merge(L, ret), R); } inline void print(node *u){ if(u->lc) pushDown(u->lc), print(u->lc); cout<<u->value<<" "; if(u->rc) pushDown(u->rc), print(u->rc); } inline node* Modify(node *u, int l, int r){ node *L, *R, *p, *q; Split(u, l - 1, L, R); Split(R, r - l + 1, p, q); p->reverse ^= 1; return Merge(L, Merge(p, q)); } int main(){ // freopen("h.in", "r", stdin); cin>>n>>m; scanf("\n"); for(int i = 1; i <= n; i++) root = Insert(root, i); for(int i = 1; i <= m; i++){ int a, b; cin>>a>>b; scanf("\n"); Modify(root, a, b); } print(root); return 0; }
【bzoj3224普通平衡树】
Description
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
Input
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
Output
对于操作3,4,5,6每行输出一个数,表示对应答案
Sample Input
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
Sample Output
84185
492737
HINT
【题目分析】
求前驱后继见代码。其余都是基本操作。
【CODE】
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<cmath> #include<algorithm> using namespace std; const int N = 1e5 + 5; #define SZ(x) (x ? x->sze : 0) inline int Rand(){ static int RAND_VAL = 1388593021; return RAND_VAL += RAND_VAL << 2 | 1; } struct node{ node *lc, *rc; int pri, val, sze; inline node* upt(){ sze = SZ(lc) + SZ(rc) + 1; return this; } }pool[N], *root, *tail = pool; inline node* Merge(node *a, node *b){ if(!a) return b->upt(); if(!b) return a->upt(); if(a->pri < b->pri){ a->rc = Merge(a->rc, b); return a->upt(); } else{ b->lc = Merge(a, b->lc); return b->upt(); } } inline void Split_k(node *u, int k, node *&x, node *&y){ if(!u){ x = y = NULL; return; } if(SZ(u->lc) < k){ Split_k(u->rc, k - SZ(u->lc) - 1, x, y); u->rc = NULL; u->upt(); x = Merge(u, x); } else{ Split_k(u->lc, k, x, y); u->lc = NULL; u->upt(); y = Merge(y, u); } } inline void Split_v(node *u, const int &v, node *&x, node *&y){ if(!u){ x = y = NULL; return; } if(u->val <= v){ Split_v(u->rc, v, x, y); u->rc = NULL; u->upt(); x = Merge(u, x); } else{ Split_v(u->lc, v, x, y); u->lc = NULL; u->upt(); y = Merge(y, u); } } inline node *newnode(const int &v){ node *x = tail++; x->pri = Rand(), x->val = v; x->lc = x->rc = NULL; x->sze = 1; return x; } inline node* Insert(const int &v){ node *L, *R; Split_v(root, v, L, R); node *newN = newnode(v); return Merge(Merge(L, newN), R); } inline node *Delete(node *u, const int &v){ node *L, *R, *p, *q, *a, *b; Split_v(u, v, L, R); Split_v(L, v - 1, p, q); if(SZ(q) > 1){ Split_k(q, SZ(q) - 1, a, b); return Merge(Merge(p, a), R); } else return Merge(p, R); } inline int getPre(node *u, const int &v){ node *L, *R; Split_v(u, v - 1, L, R); node *tmp = L; if(tmp) while(tmp->rc) tmp = tmp->rc; u = Merge(L, R); return tmp->val; } inline int getSuf(node *u, const int &v){ node *L, *R; Split_v(u, v, L, R); node *tmp = R; if(tmp) while(tmp->lc) tmp = tmp->lc; u = Merge(L, R); return tmp->val; } inline int query_v_Rank(node *u, int v){ node *L, *R; Split_v(u, v - 1, L, R); int ret = SZ(L) + 1; u = Merge(L, R); return ret; } inline int query_Rank_v(node *u, int k){ node *y = u; while(y){ int rank = SZ(y->lc) + 1; if(rank == k) return y->val; if(rank > k) y = y->lc; else{ y = y->rc; k -= rank; } } } int n, opt, x; int main(){ ios::sync_with_stdio(false); cin.tie(NULL); cin>>n; for(int i = 1; i <= n; i++){ cin>>opt>>x; switch(opt){ case 1: root = Insert(x); break; case 2: root = Delete(root, x); break; case 3: cout<<query_v_Rank(root, x)<<endl; break; case 4: cout<<query_Rank_v(root, x)<<endl; break; case 5: cout<<getPre(root, x)<<endl; break; case 6: cout<<getSuf(root, x)<<endl; break; } } return 0; }
【poj3580】SuperMemo
Description
Your friend, Jackson is invited to a TV show called SuperMemo in which the participant is told to play a memorizing game. At first, the host tells the participant a sequence of numbers, {A1, A2, ... An}. Then the host performs a series of operations and queries on the sequence which consists:
- ADD x y D: Add D to each number in sub-sequence {Ax ... Ay}. For example, performing "ADD 2 4 1" on {1, 2, 3, 4, 5} results in {1, 3, 4, 5, 5}
- REVERSE x y: reverse the sub-sequence {Ax ... Ay}. For example, performing "REVERSE 2 4" on {1, 2, 3, 4, 5} results in {1, 4, 3, 2, 5}
- REVOLVE x y T: rotate sub-sequence {Ax ... Ay} T times. For example, performing "REVOLVE 2 4 2" on {1, 2, 3, 4, 5} results in {1, 3, 4, 2, 5}
- INSERT x P: insert P after Ax. For example, performing "INSERT 2 4" on {1, 2, 3, 4, 5} results in {1, 2, 4, 3, 4, 5}
- DELETE x: delete Ax. For example, performing "DELETE 2" on {1, 2, 3, 4, 5} results in {1, 3, 4, 5}
- MIN x y: query the participant what is the minimum number in sub-sequence {Ax ... Ay}. For example, the correct answer to "MIN 2 4" on {1, 2, 3, 4, 5} is 2
To make the show more interesting, the participant is granted a chance to turn to someone else that means when Jackson feels difficult in answering a query he may call you for help. You task is to watch the TV show and write a program giving the correct answer to each query in order to assist Jackson whenever he calls.
Input
The first line contains n (n ≤ 100000).
The following n lines describe the sequence.
Then follows M (M ≤ 100000), the numbers of operations and queries.
The following M lines describe the operations and queries.
Output
Sample Input
5
1
2
3
4
5
2
ADD 2 4 1
MIN 4 5
Sample Output
5
【题目分析】
所有操作都可以用无旋式treap完成
【CODE】
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<algorithm> #include<cmath> using namespace std; const int N = 1000500; int n, M; #define SZ(x) (x?x->sze:0) inline void Wr(int); inline int Re(); struct node{ node *lc, *rc; int pri, val, sze; int add, minn; bool rev; inline void upt(){ sze = SZ(lc) + SZ(rc) + 1; minn = val; if(lc) minn = min(minn, lc->minn + lc->add); if(rc) minn = min(minn, rc->minn + rc->add); return; } inline void PushDown(){ if(rev){ if(lc) lc->rev ^= 1; if(rc) rc->rev ^= 1; swap(lc, rc); rev = 0; } if(add){ minn += add; val += add; if(lc) lc->add += add; if(rc) rc->add += add; add = 0; } upt(); } inline void print(){ if(lc) lc->print(); PushDown(); Wr(val), putchar(' '); if(rc) rc->print(); } }pool[N], *root, *tail = pool; inline int Rand(){ static int RAND_VAL = 1388593021; return RAND_VAL += RAND_VAL << 2 | 1; } inline node* NewNode(const int &v){ node *x = tail++; x->val = v; x->pri = Rand(); x->lc = x->rc = NULL; x->minn = v; x->sze = 1; x->rev = 0; return x; } inline node* Merge(node *u, node *v){ if(!u){ v->upt(); return v; } if(!v){ u->upt(); return u; } u->PushDown(), v->PushDown(); u->upt(), v->upt(); if(u->pri < v->pri){ u->rc = Merge(u->rc, v); u->upt(); return u; } else{ v->lc = Merge(u, v->lc); v->upt(); return v; } } inline void Split_k(node *u, int k, node *&x, node *&y){ if(!u){ x = y = NULL; return; } u->PushDown(); u->upt(); if(SZ(u->lc) < k){ Split_k(u->rc, k - SZ(u->lc) - 1, x, y); u->rc = NULL, u->upt(); x = Merge(u, x); } else{ Split_k(u->lc, k, x, y); u->lc = NULL, u->upt(); y = Merge(y, u); } } inline int Re(){ int x = 0, f = 1; char ch = getchar(); for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar()); if(ch == '-') f = -1, ch = getchar(); for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + (ch - '0'); return x * f; } inline void Wr(int x){ if(x < 0) putchar('-'), x = -x; if(x > 9) Wr(x / 10); putchar(x % 10 + '0'); } inline void Reverse(const int &l, const int &r){ node *L, *R, *p, *q; Split_k(root, l - 1, L, R); Split_k(R, r - l + 1, p, q); if(p)p->rev ^= 1; root = Merge(L, Merge(p, q)); } inline void AddTo(const int &l, const int &r, const int &v){ node *L, *R, *p, *q; Split_k(root, l - 1, L, R); Split_k(R, r - l + 1, p, q); if(p)p->add += v; root = Merge(L, Merge(p, q)); } inline node* Insert(const int &k, const int &v){ node *L, *R; Split_k(root, k, L, R); node *res = NewNode(v); return Merge(Merge(L, res), R); } inline void Revolve(const int &l, const int &r, const int &k){ if(!(k % (r - l + 1))) return; node *L, *R, *p, *q, *a, *b; Split_k(root, l - 1, L, R); Split_k(R, r - l + 1, p, q); int o = r - l + 1; Split_k(p, o - (k % o), a, b); p = Merge(b, a); root = Merge(L, Merge(p, q)); } inline void Delete(const int &k){ node *L, *R, *p, *q; Split_k(root, k - 1, L, R); Split_k(R, 1, p, q); root = Merge(L, q); } inline int Min(const int &l, const int &r){ node *L, *R, *p, *q; root->PushDown(); Split_k(root, l - 1, L, R); R->PushDown(); Split_k(R, r - l + 1, p, q); if(p)p->PushDown(); int ret = p->minn; root = Merge(L, Merge(p, q)); return ret; } int main(){ // freopen("h.in", "r", stdin); scanf("%d", &n); for(int i = 1; i <= n; i++) root = Insert(i, Re()); // root->print(); scanf("%d", &M); char opt[10]; for(int i = 1; i <= M; i++){ scanf("%s", opt + 1); if(opt[1] == 'A'){ //add int l = Re(), r = Re(), v = Re(); AddTo(l, r, v); } else if(opt[1] == 'R'){ if(opt[4] == 'E'){ //reverse int l = Re(), r = Re(); Reverse(l, r); } else if(opt[4] == 'O'){ //revolve int l = Re(), r = Re(), k = Re(); Revolve(l, r, k); } } else if(opt[1] == 'I'){ //insert int k = Re(), v = Re(); root = Insert(k, v); } else if(opt[1] == 'M'){ //min int l = Re(), r = Re(); printf("%d\n", Min(l, r)); } else if(opt[1] == 'D'){ //delete int k = Re(); Delete(k); } // root->print(), cout<<endl; } return 0; }
【noi2003】文本编辑器
题目背景
NOI2003 DAY1 T2
题目描述
很久很久以前,DOS3.x 的程序员们开始对 EDLIN 感到厌倦。于是,人们开始纷纷改用自己写的文本编辑器……
多年之后,出于偶然的机会,小明找到了当时的一个编辑软件。进行了一些简单的测试后,小明惊奇地发现:那个软件每秒能够进行上万次编辑操作(当然,你不能手工进行这样的测试)!于是,小明废寝忘食地想做一个同样的东西出来。你能帮助他吗?
为了明确任务目标,小明对“文本编辑器”做了一个抽象的定义:
文本:由 0 个或多个字符构成的序列。这些字符的 ASCII 码在闭区间 [32, 126] 内,也就是说,这些字符均为可见字符或空格。
光标:在一段文本中用于指示位置的标记,可以位于文本的第一个字符之前,文本的最后一个字符之后或文本的某两个相邻字符之间。
文本编辑器:为一个可以对一段文本和该文本中的一个光标进行如下六条操作的程序。如果这段文本为空,我们就说这个文本编辑器是空的。
比如从一个空的文本编辑器开始,依次执行操作INSERT(13, “Balanced□tree”),MOVE(2),DELETE(5),NEXT(),INSERT(7, “□editor”),MOVE(0),GET(15)后,会输出“Bad□editor□tree”,如下表所示:
上表中,“|”表示光标,“□”表示空格。
你的任务是:
-
- 建立一个空的文本编辑器。
- 从输入文件中读入一些操作指令并执行。
- 对所有执行过的 GET 操作,将指定的内容写入输出文件。
输入格式
输入文件的第一行是指令条数 t ,以下是需要执行的 t 个操作。其中:
为了使输入文件便于阅读,Insert 操作的字符串中可能会插入一些回车符,请忽略掉它们(如果难以理解这句话,可以参考样例)。
除了回车符之外,输入文件的所有字符的 ASCII码 都在闭区间 [32, 126] 内。且行尾没有空格。
这里我们有如下假定:
MOVE 操作不超过 50000 个,INSERT 和 DELETE 操作的总个数不超过 4000 ,PREV 和 NEXT 操作的总个数不超过 200000 。
所有 INSERT 插入的字符数之和不超过 2M(1M=1024*1024),正确的输出文件长度不超过 3M 字节。
DELETE 操作和 GET 操作执行时光标后必然有足够的字符。MOVE、PREV、NEXT 操作不会把光标移动到非法位置。
输入文件没有错误。
对 C++ 选手的提示:经测试,对最大的测试数据使用 fstream 进行输入有可能会比使用 stdio 慢约 1 秒,因此建议在可以的情况下使用后者。
输出格式
输出多行,每行依次对应输入文件中每条 GET 指令的输出。
样例数据 1
输入
15
Insert 26
abcdefghijklmnop
qrstuv wxy
Move 15
Delete 11
Move 5
Insert 1
^
Next
Insert 1
_
Next
Next
Insert 4
.\/.
Get 4
Prev
Insert 1
^
Move 0
Get 22
输出
.\/.
abcde^_^f.\/.ghijklmno
【题目分析】
无旋式treap
【CODE】
#include<iostream> #include<cstring> #include<string> #include<cmath> #include<cstdio> #include<cstdlib> #include<algorithm> using namespace std; #define SZ(x) (x?x->sze:0) const int N = 1e6; struct node{ node *lc, *rc; int pri, sze; char val; inline node* upt(){ sze = SZ(lc) + SZ(rc) + 1; return this; } inline void print(){ if(lc) lc->print(); putchar(val); if(rc) rc->print(); } }pool[(N << 2) + 1000], *root, *tail = pool; int t, pos; inline int Re(){ int x = 0, f = 1; char ch = getchar(); for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar()); if(ch == '-') f = -1, ch = getchar(); for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + (ch - '0'); return x * f; } inline void Wr(int x){ if(x < 0) putchar('-'), x = -x; if(x > 9) Wr(x / 10); putchar(x % 10 + '0'); } inline node* Merge(node *u, node *v){ if(!u) return v; if(!v) return u; if(u->pri < v->pri){ u->rc = Merge(u->rc, v); return u->upt(); } else{ v->lc = Merge(u, v->lc); return v->upt(); } } inline void Split_k(node *u, int k, node *&x, node *&y){ if(!u){ x = y = NULL; return ; } if(SZ(u->lc) < k){ Split_k(u->rc, k - SZ(u->lc) - 1, x, y); u->rc = NULL, u->upt(); x = Merge(u, x); } else{ Split_k(u->lc, k, x, y); u->lc = NULL, u->upt(); y = Merge(y, u); } } inline int Rand(){ static int RAND_VAL = 1388593021; return RAND_VAL += RAND_VAL << 2 | 1; } inline node* NewNode(const char &v){ node *x = tail++; x->val = v; x->pri = Rand(); x->sze = 1; x->lc = x->rc = NULL; return x; } inline node* build(char *a, int length){ static node *stack[(N << 2) + 1000], *pre, *u; int top = 0; for(int i = 1; i <= length; i++){ u = NewNode(a[i]); pre = NULL; while(top && stack[top]->pri > u->pri){ pre = stack[top]->upt(); stack[top--] = NULL; } if(top) stack[top]->rc = u; u->lc = pre; stack[++top] = u; } while(top) stack[top--]->upt(); return stack[1]; } char q[(N << 2) + 1000]; inline node* Insert(const int &k, const int &l){ if(l == 0) return root; node *L, *R; Split_k(root, k, L, R); for(int i = 1; i <= l; i++){ char c; c = getchar(); while(c < 32 || c > 126) c = getchar(); q[i] = c; } node *tmp = build(q, l); return Merge(Merge(L, tmp), R); } inline node* Delete(const int &l, const int &r){ node *L, *R, *p, *q; Split_k(root, l - 1, L, R); Split_k(R, r - l + 1, p, q); return Merge(L, q); } inline void PrintStr(const int &l, const int &r){ node *L, *R, *p, *q; Split_k(root, l - 1, L, R); Split_k(R, r - l + 1, p, q); if(p) p->print(), cout<<endl; root = Merge(L, Merge(p, q)); } int main(){ t = Re(); pos = 0; for(int i = 1; i <= t; i++){ char opt[15]; scanf("%s", opt + 1); if(opt[1] == 'I'){ int len = Re() ,tmp = pos; root = Insert(pos, len); pos = tmp; } else if(opt[1] == 'M') pos = Re(); else if(opt[1] == 'D'){ int k = Re(); root = Delete(pos + 1, pos + k); } else if(opt[1] == 'G'){ int k = Re(); PrintStr(pos + 1, pos + k); } else if(opt[1] == 'P') pos--; else if(opt[1] == 'N') pos++; } return 0; }
【维护数列】
题目背景
NOI2005 DAY1 T2
题目描述
请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏中的下划线‘_’表示实际输入文件中的空格)
输入格式
第 1 行包含两个数 N 和 M ,N 表示初始时数列中数的个数,M 表示要进行的操作数目。
第 2 行包含 N 个数字,描述初始时的数列。
以下 M 行,每行一条命令,格式参见问题描述中的表格。
输出格式
对于输入数据中的 GET-SUM 和 MAX-SUM 操作,向输出文件依次打印结果,每个答案(数字)占一行。
样例数据 1
输入
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
输出
-1
10
1
10
备注
【样例说明】
初始时,我们拥有数列:
2 -6 3 5 1 -5 -3 6 3
执行操作 GET-SUM 5 4 ,表示求出数列中从第 5 个数开始连续 4 个数字之和,如下图中的灰色部分 1+(-5)+(-3)+6 = -1:
2 -6 3 5 1 -5 -3 6 3
执行操作 MAX-SUM ,表示要求求出当前数列中最大的一段和,即如下图所示,应为 3+5+1+(-5)+(-3)+6+3 = 10:
2 -6 3 5 1 -5 -3 6 3
执行操作 INSERT 8 3 -5 7 2,即在数列中第 8 个数字后插入 -5 7 2,如下所示的灰色部分:
2 -6 3 5 1 -5 -3 6 -5 7 2 3
执行操作 DELETE 12 1,表示删除第 12 个数字,即最后一个:
2 -6 3 5 1 -5 -3 6 -5 7 2
执行操作 MAKE-SAME 3 3 2 ,表示从第 3 个数开始的 3 个数字,即下图中的灰色部分,统一修改为 2 :
2 -6 3 5 1 -5 -3 6 -5 7 2
改为
2 -6 2 2 2 -5 -3 6 -5 7 2
执行操作 REVERSE 3 6,表示取出数列中从第 3 个数开始的连续 6 个数:
如上所示的灰色部分 2 2 2 -5 -3 6 ,翻转后得到 6 -3 -5 2 2 2 ,并放回原来位置:
2 -6 6 -3 -5 2 2 2 -5 7 2
最后执行 GET-SUM 5 4 和 MAX-SUM ,不难得到答案 1 和 10 。
【评分方法】
本题设有部分分,对于每一个测试点:
-
- 如果你的程序能在输出文件正确的位置上打印 GET-SUM 操作的答案,你可以得到该测试点 60% 的分数;
- 如果你的程序能在输出文件正确的位置上打印 MAX-SUM 操作的答案,你可以得到该测试点 40% 的分数;
- 以上两条的分数可以叠加,即如果你的程序正确输出所有 GET-SUM 和 MAX-SUM 操作的答案,你可以得到该测试点 100% 的分数。
请注意:如果你的程序只能正确处理某一种操作,请确定在输出文件正确的位置上打印结果,即必须为另一种操作留下对应的行,否则我们不保证可以正确评分。
【数据规模和约定】
你可以认为在任何时刻,数列中至少有 1 个数。
输入数据一定是正确的,即指定位置的数在数列中一定存在。
50% 的数据中,任何时刻数列中最多含有 30,000 个数;
100% 的数据中,任何时刻数列中最多含有 500,000 个数。
100% 的数据中,任何时刻数列中任何一个数字均在 [-1,000,1,000] 内 。
100% 的数据中,M≤20,000,插入的数字总数不超过 4,000,000 个,输入文件大小不超过 20 MBytes 。
【题目分析】
除了子串和,其他都没什么难的。
通过本题也学到了求最大子串和的方法:多记录$lx(从左端起最大和), rx(从右端起最大和), mx(最大子串和)$
更新时(当前节点为$k$, 左儿子$lc$, 右儿子$rc$):
$k->lx = max(lc->lx, lc->sum + rc->lx)$
rx同理
【CODE】
AC代码。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<algorithm> #include<ctime> #include<cmath> #include<vector> using namespace std; const int N = 500050, oo = 10000; #define SZ(x) (x?x->sze:0) #define SUM(x) (x?x->sum:0) #define LX(x) (x?x->lx:-oo) #define RX(x) (x?x->rx:-oo) #define MX(x) (x?x->mx:-oo) struct node{ node *lc, *rc; int pri, val, sze; int rev, tag; int lx, rx, mx; int sum; inline node* upt(){ sze = SZ(lc) + SZ(rc) + 1; sum = SUM(lc) + val + SUM(rc); lx = max(LX(lc), SUM(lc)+ val + max(0, LX(rc))); rx = max(RX(rc), SUM(rc) + val + max(0, RX(lc))); mx = max(0, LX(rc)) + val + max(0, RX(lc)); mx = max(mx, max(MX(lc), MX(rc))); return this; } inline void pushDown(){ if(rev){ if(lc)lc->reverse(); if(rc)rc->reverse(); rev = 0; } if(tag != 1111){ if(lc) lc->cover(tag); if(rc) rc->cover(tag); tag = 1111; } } inline void print(){ if(lc) lc->print(); pushDown(), upt(), cout<<val<<" "; if(rc) rc->print(); } inline void cover(const int &v){ val = v, sum = v * sze; lx = rx = mx = max(sum, v); tag = v; } inline void reverse(){ swap(lc, rc); swap(lx, rx); rev ^= 1; } }pool[N], *root, *tail = pool, *rec[N]; int n, m, recTop; inline int Rand(){ static int RAND_VAL = 1388593021; return RAND_VAL += RAND_VAL << 2 | 1; } inline int Re(){ int i = 0, f = 1; char ch = getchar(); for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar()); if(ch == '-') f = -1, ch = getchar(); for(; ch >= '0' && ch <= '9'; ch = getchar()) i = (i << 3) + (i << 1) + (ch - '0'); return i * f; } inline void Wr(int x){ if(x < 0) putchar('-'), x = -x; if(x > 9) Wr(x / 10); putchar(x % 10 + '0'); } inline void recycle(node *u){ rec[++recTop] = u; } inline void clear(node *u){ if(u->lc) clear(u->lc); if(u->rc) clear(u->rc); recycle(u); } inline node* NewNode(const int &v){ node *x; if(recTop) x = rec[recTop--]; else x = tail++; x->pri = rand(); x->val = x->sum = x->lx = x->rx = x->mx = v; x->sze = 1; x->lc = x->rc = NULL; x->rev = 0; x->tag = 1111; return x; } inline node* Merge(node *u, node *v){ if(!u) return v; if(!v) return u; if(u->pri < v->pri){ u->pushDown(); u->rc = Merge(u->rc, v); return u->upt(); } else{ v->pushDown(); v->lc = Merge(u, v->lc); return v->upt(); } } inline void Split_k(node *u, const int &k, node *&x, node *&y){ if(!u){ x = y = NULL; return; } u->pushDown(); if(SZ(u->lc) < k){ Split_k(u->rc, k - SZ(u->lc) - 1, x, y); u->rc = NULL, u->upt(); x = Merge(u, x); } else{ Split_k(u->lc, k, x, y); u->lc = NULL, u->upt(); y = Merge(y, u); } } inline node* build(int*, int); int t[N]; inline node* Insert(const int &pos, const int &l){ node *L, *R; Split_k(root, pos, L, R); for(int i = 1; i <= l; i++) t[i] = Re(); node *tmp = build(t, l); return Merge(Merge(L, tmp), R); } inline node* Delete(const int &l, const int &r){ node *L, *R, *p, *q; Split_k(root, l - 1, L, R); Split_k(R, r - l + 1, p, q); if(p) clear(p); return Merge(L, q); } inline node* MakeSame(const int &l, const int &r, const int &v){ node *L, *R, *p, *q; Split_k(root, l - 1, L, R); Split_k(R, r - l + 1, p, q); if(p) p->cover(v); return Merge(L, Merge(p, q)); } inline void PrintSum(const int &l, const int &r){ node *L, *R, *p, *q; Split_k(root, l - 1, L, R); Split_k(R, r - l + 1, p, q); int ret = 0; if(p) ret = SUM(p); root = Merge(L, Merge(p, q)); Wr(ret), putchar('\n'); } inline node* Reverse(const int &l, const int &r){ node *L, *R, *p, *q; Split_k(root, l - 1, L, R); Split_k(R, r - l + 1, p, q); if(p) p->reverse(); return Merge(L, Merge(p, q)); } inline node* build(int *a, int len){ static node *stack[N], *pre, *u; int top = 0; for(int i = 1; i <= len; i++){ u = NewNode(a[i]); pre = NULL; while(top && stack[top]->pri > u->pri){ pre = stack[top]->upt(); stack[top--] = NULL; } if(top) stack[top]->rc = u; u->lc = pre; stack[++top] = u; } while(top) stack[top--]->upt(); return stack[1]; } int main(){ // freopen("h.in", "r",stdin); n = Re(), m = Re(); Rand(); int a[N]; for(int i = 1; i <= n; i++) a[i] = Re(); root = build(a, n); for(int i = 1; i <= m; i++){ char opt[15]; int pos, tot, v; scanf("%s", opt + 1); if(opt[1] == 'I'){ pos = Re(), tot = Re(); root = Insert(pos, tot); } else if(opt[1] == 'D'){ pos = Re(), tot = Re(); root = Delete(pos, pos + tot - 1); } else if(opt[1] == 'M'){ if(opt[3] == 'K'){ pos = Re(), tot = Re(), v = Re(); root = MakeSame(pos, pos + tot - 1, v); } else{ Wr(root->mx), putchar('\n'); } } else if(opt[1] == 'R'){ pos = Re(), tot = Re(); root = Reverse(pos, pos + tot - 1); } else if(opt[1] == 'G'){ pos = Re(), tot = Re(); PrintSum(pos, pos + tot - 1); } // root->print();cout<<endl; } return 0; }