关于平衡树的练习,以后关于平衡树的题都放这儿了
1、HNOI2002营业额统计:古董题了,题目大意就是维护一个数列,只有一个操作,在数列中插入一个数,计算出数列中与这个数之差的最小绝对值,统计总和。
裸的平衡树模板题,方便一点可以用set+lower_boud+upper_bound秒杀,我写了一下splay,速度还凑活
2、NOI2004郁闷的出纳员:题目大意是维护一个数列,并且支持5种操作,1、插入一个元素,2、删除值小于标准的元素,3、数列中的每一个数的值都增加A,4、数列中的每一个数的值都减小A.,5、输出工资第K大的人的工资数目
这道题也可以用平衡树来维护,由于它的增加减少操作的特殊性(数列中的每个元素都要进行增加或者减少),所以我们不能傻乎乎的一个个搞,可以在外部记录一个变量,表示当前工资的改变量,当一个数要插入时,把这个数减去工资改变量之后插入,当你要输出该元素工资时,可以吧数列中要输出的元素加上工资的改变量,如果你要删除的时候,你可以去删除小于最低工资标准减去工资改变量的元素(这样类似与前缀和优化)。剩下的操作就类似与平衡树的细节了。
#include <cstdio> #include <cstdlib> using namespace std; const int maxn = 100000; int money,n,v,change,numm,leave=0; char c;bool first = true; struct node{ int key,left,right,lson,rson,father; }; class splayTree{ public: void zig(int x){ int y = tree[x].father; tree[y].left = tree[x].right; if (tree[x].right!=0) tree[tree[x].right].father = y; tree[x].father = tree[y].father; if (tree[y].father!=0){ if (y == tree[tree[y].father].left) tree[tree[y].father].left = x; else tree[tree[y].father].right = x; } tree[x].right = y; tree[y].father = x; tree[y].lson = tree[x].rson; tree[x].rson = tree[y].lson + tree[y].rson + 1; } void zag(int x){ int y = tree[x].father; tree[y].right = tree[x].left; if (tree[x].left!=0) tree[tree[x].left].father = y; tree[x].father = tree[y].father; if (tree[y].father!=0){ if (y == tree[tree[y].father].left) tree[tree[y].father].left = x; else tree[tree[y].father].right = x; } tree[x].left = y; tree[y].father = x; tree[y].rson = tree[x].lson; tree[x].lson = tree[y].lson + tree[y].rson + 1; } void splay(int now){ while (tree[now].father!=0){ int t = tree[now].father; if (tree[t].father == 0){ if (now == tree[t].left) zig(now); else zag(now); break; } if (now == tree[t].left){ if (t == tree[tree[t].father].left) zig(t),zig(now); else zig(now),zag(now); }else{ if (t == tree[tree[t].father].right) zag(t),zag(now); else zag(now),zig(now); } } root = now; } void insert(int x){ if (numm==0){ tree[++num].key = x; numm=1;root = num; return; } int t = root; tree[++num].key = x; numm++; while (true){ if (x<=tree[t].key){ if (tree[t].left==0){ tree[t].left = num;tree[t].lson ++ ; tree[num].father = t; break; }else t = tree[t].left; }else{ if (tree[t].right == 0){ tree[t].right = num;tree[t].rson ++; tree[num].father = t; break; }else t = tree[t].right; } } while (tree[t].father!=0){ if (t == tree[tree[t].father].left) tree[tree[t].father].lson++; else tree[tree[t].father].rson++; t = tree[t].father; } splay(num); } void del(int x){ insert(x); numm=numm-tree[root].lson-1; leave+=tree[root].lson; root = tree[root].right; tree[root].father = 0; } void find(int x){ int t = root; if (x<=0 || x>tree[t].lson+tree[t].rson+1) {printf("-1\n");return;} while (true){ if (x>=tree[t].lson+1){ if (x == tree[t].lson+1){ printf("%d\n",tree[t].key+change); break; }else {x=x-(tree[t].lson+1);t = tree[t].right;} }else t = tree[t].left; } } node tree[maxn]; int num,root; }spt; int main(){ freopen("cashier.in","r",stdin); freopen("cashier.out","w",stdout); scanf("%d %d\n",&n,&money); for (int i=1;i<=n;++i){ scanf("%c %d\n",&c,&v); switch (c){ case 'I':if (v>=money) spt.insert(v-change);break; case 'A':change+=v;break; case 'S':change-=v;spt.del(money-change);break; case 'F':spt.find(numm-v+1);break; } }printf("%d\n",leave); return 0; }
题目有一个性质,就是如果你是人去领养宠物,如果收养所里有宠物,那么你可以立即收养,如果没有宠物,那么你人就会在收养所里预订一个名额,下次如果有宠物进来了,那么你可以优先选择,这就保证了宠物收养所里面要不都是宠物,要不都是人的预订名额。所以我们可以给这个数列设定一个标记,表示收养所里的是宠物还是人的预订。根据不同的新插入元素,对于不同的标记进行不同的操作,这题用不着写个冗长的平衡树,用set+lower_bound搞搞差不多了,400+字节可以AC,速度当然比手写的平衡树要慢一点。。。
4、ZOJI2007报表统计:题目大意是有一个数列,数列每个元素都是一个链表,并把这些链表按照数列的顺序顺次链接起来形成一个大的链表。该问题要支持3种操作,1、在某一个元素代表的链表的末尾插入一个元素,2、询问数列中相邻2个数的差值的绝对值的最小值,3、询问数列中任意2个数的差值的绝对值的最小值。
第二个操作我们可以通过一个multiset来完成,每插入一个元素,显而易见,会形成2个新的差值,把这两个差值加入multiset,另一个方面,当你加入一个元素的时候,也会使原来存在的一个差值不存在(详见题目),我们只需要知道这个差值是多少,然后在multiset中用find函数找到它,并用erase删除就可以了。
第三个操作我们可以知道任意两个数的差值最小必然存在于这两个数在排序后的数列中是相邻的,因此我们用splay来维护这些数,每次插入一个数,都在splay中求前趋和后继,然后更新一下目前最小值,一个优化就是,如果你当前的最小值已经为0了,那么下一次插入的时候就不用去找前趋后继之类来更新最小值了。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <set> #include <algorithm> using namespace std; multiset<int>::iterator it; const int maxn = 500000+10,INF = 0x7FFFFFFF; int A[maxn],B[maxn],l,r; int MIN_GAP = INF,MIN_SORT_GAP = INF; int succ,prev,num = 1,root,n,m; struct node{ int father,left,right,lson,rson,key; }; class splayTree{ public: void zig(int x){ int y = tree[x].father; tree[y].left = tree[x].right; if (tree[x].right !=0) tree[tree[x].right].father = y; tree[x].father = tree[y].father ; if (tree[y].father!=0){ if (y == tree[tree[y].father].left) tree[tree[y].father].left = x; else tree[tree[y].father].right = x; } tree[x].right = y; tree[y].father = x; } void zag(int x){ int y = tree[x].father; tree[y].right = tree[x].left; if (tree[x].left!=0) tree[tree[x].left].father = y; tree[x].father = tree[y].father; if (tree[y].father!=0){ if (y == tree[tree[y].father].left) tree[tree[y].father].left = x; else tree[tree[y].father].right = x; } tree[x].left = y; tree[y].father = x; } void splay(int now){ while (tree[now].father !=0){ int t = tree[now].father; if (tree[t].father==0){ if (now == tree[t].left) zig(now); else zag(now); break; } else{ if (now == tree[t].left){ if (t == tree[tree[t].father].left) zig(t),zig(now); else zig(now),zag(now); }else{ if (t == tree[tree[t].father].right) zag(t),zag(now); else zag(now),zig(now); } } } root = now; } void insert(int x){ tree[++num].key = x; int t = root; while (true){ if (x<=tree[t].key){ if (tree[t].left==0){ tree[t].left = num; tree[num].father = t; break; }else t = tree[t].left; }else{ if (tree[t].right == 0){ tree[t].right = num; tree[num].father = t; break; }else t = tree[t].right; } } splay(num); } int prev(){ int t = tree[root].left; if (t == 0) return INF/2; while (tree[t].right!=0) t = tree[t].right; return tree[t].key; } int succ(){ int t = tree[root].right; if (t == 0) return INF/2; while (tree[t].left!=0) t = tree[t].left; return tree[t].key; } node tree[maxn]; }spt; int main(){multiset<int> s; scanf("%d %d\n",&n,&m); scanf("%d",&A[1]); spt.tree[1].key = A[1];root = 1;B[1] = A[1]; for (int i=2;i<=n;++i){ scanf("%d",&A[i]);B[i] = A[i]; s.insert(abs(A[i]-A[i-1])); spt.insert(A[i]);prev = spt.prev();succ = spt.succ(); MIN_SORT_GAP = min(MIN_SORT_GAP,min(abs(succ-A[i]),abs(A[i]-prev))); }getchar();MIN_GAP = *s.begin(); for (int i=1,a,b;i<=m;++i){ char op[20]; scanf("%s",op+1); scanf("%d %d\n",&a,&b); if (op[1] == 'I'){ it = s.find(abs(A[a]-B[a+1])); if (it!=s.end()) s.erase(it); s.insert(abs(B[a+1]-b));s.insert(abs(A[a]-b)); MIN_GAP = min(abs(A[a]-b),*s.begin()); A[a] = b; if (MIN_SORT_GAP){ spt.insert(b);prev = spt.prev();succ = spt.succ(); MIN_SORT_GAP = min(MIN_SORT_GAP,min(abs(succ-A[a]),abs(A[a]-prev))); } }else{ if (op[5] == 'G') printf("%d\n",MIN_GAP); else printf("%d\n",MIN_SORT_GAP); } }return 0; }抱怨一下:GNU风格的C++代码为什么缩进在CSDN显示不出来。。。。。
5、sgu187 : 题目大意就是给出一个序列,并给出几个操作,每个操作都是把[a,b]的数进行取反,就是原来从左到右,变成现在的从右到左,看了几篇论文,其实splay也是可以支持区间操作的,所以类似线段数一样打一下标记就直接上就可以了,唯一恶心的是sgu上的内存限制只有4M,我一开是动态开指针内存目测要挂,于是就先开了一个数组,通过引用数组的地址来实现指针。
#include <iostream> #include <cstdio> #include <cstdlib> #define keyTree root->ch[1]->ch[0] using namespace std; const int maxn = 130100,INF = 0x7FFFFFFF/2; int n,m; struct node{ int key,size; bool rev; node *pre,*ch[2]; void reverse(){ if (size == 0) return; rev ^= 1; swap(ch[0],ch[1]); } void push_up(){ size = ch[0]->size + ch[1]->size + 1; } void push_down(){ if (rev){ ch[0]->reverse(); ch[1]->reverse(); }rev = 0; } }; class splayTree{ public: node *root,*null;int top; node data[maxn]; inline node *newNode(int value){ node *t; t = &data[top++]; t->key = value; t->pre = t->ch[0] = t->ch[1] = null; t->size = t->rev = 0; return t; } inline void init(){top = 0; null = newNode(INF);null->size = 0; root = newNode(INF);root->ch[1] = newNode(INF);root->ch[1]->pre=root; root->push_up(); } inline node *build(int l,int r){ if (l>r) return null; int mid = (l+r) >> 1; node *p = newNode(mid); p->ch[0] = build(l,mid-1); p->ch[1] = build(mid+1,r); if (p->ch[0] != null) p->ch[0]->pre = p; if (p->ch[1] != null) p->ch[1]->pre = p; p->push_up(); return p; } inline void rotate(node *x,int c){ node *y = x->pre; y->push_down();x->push_down(); y->ch[!c] = x->ch[c]; if (y->ch[!c]!=null) y->ch[!c]->pre = y; x->pre = y->pre; if (x->pre != null) x->pre->ch[y == y->pre->ch[1]] = x; y->pre = x; x->ch[c] = y; y->push_up(); if (y == root) root = x; } inline void splay(node *x,node *goal){ x->push_down(); while (x->pre != goal){ if (x->pre->pre == goal){ rotate(x,x == x->pre->ch[0]); break; } node *y = x->pre,*z = y->pre; int f = (y == z->ch[0]); if (x == y->ch[f]) rotate(x,!f),rotate(x,f); else rotate(y,f),rotate(x,f); } x->push_up(); } inline void select(int k,node *x){ node * t = root; while (true){ t->push_down(); int tmp = t->ch[0]->size; if (tmp == k) break; if (tmp < k){ k-=tmp+1; t = t->ch[1]; }else t = t->ch[0]; } splay(t,x); } inline void reverse(int l,int r){ select(l-1,null); select(r+1,root); keyTree->reverse(); } inline void print(node *t){ if (t == null) return; t->push_down(); print(t->ch[0]); if (t->key != INF) printf("%d ",t->key); print(t->ch[1]); } }spt; int main(){ freopen("test.in","r",stdin); freopen("test.out","w",stdout); scanf("%d %d\n",&n,&m); spt.init(); spt.keyTree = spt.build(1,n); spt.keyTree->pre = spt.root->ch[1]; spt.splay(spt.keyTree,spt.null); for (int i=1,a,b;i<=m;++i) scanf("%d %d\n",&a,&b),spt.reverse(a,b); spt.print(spt.root); printf("\n"); return 0; }
6、APIO2012派遣:这道题目很新鲜吧。
派遣
【问题描述】
在一个忍者的帮派里,些们被选中遣给 在一个忍者的帮派里,些们被选中遣给 顾客,然后依据自己的工作 顾客,然后依据自己的工作 获取 报偿。
在这个帮派里,有一名忍者被称之为 Master Master。除了 。除了 Master Master以外,每名忍者 以外,每名忍者 都有且仅一个 上级 。为保密 。为保密 ,同时增强 ,同时增强 忍者们的领导力, 所有 与他们工作相关 的指令总是由 上级发送给他的 直接 下属,而不允许通过其他的方式发送。
现在你 要招募 一批忍者,并把它 一批忍者,并把它 们派遣给 顾客。你需要为 顾客。你需要为 每个 被派遣的 忍者 支付 一定的 薪水 ,同时使得支付的 ,同时使得支付的 薪水 总额不超过你的 预算。另外,为了发送指 预算。另外,为了发送指 预算。另外,为了发送指 令,你需要选择一名忍者 作为管理求这个可以向所有被派遣的令,你需要选择一名忍者 作为管理求这个可以向所有被派遣的令,你需要选择一名忍者 作为管理求这个可以向所有被派遣的发送指令 ,在发送指令时任何忍者(不管是否 ,在发送指令时任何忍者(不管是否 ,在发送指令时任何忍者(不管是否 ,在发送指令时任何忍者(不管是否 被派遣)都可以作为 派遣)都可以作为 消息的传递 人。管理者自己可 。管理者自己可 。管理者自己可 以被派遣 ,也可 ,也可 以不被派遣。当然, 如果管理者没有排不被派遣。当然, 如果管理者没有排不被派遣。当然, 如果管理者没有排不被派遣。当然, 如果管理者没有排不被派遣。当然, 如果管理者没有排不被派遣。当然, 如果管理者没有排你就不需要支付管理者的薪水。
你的目标是在预算内使顾客满意度 最大。这里定义顾客的 满意度为派遣大。这里定义顾客的 满意度为派遣忍者总数乘以管理的领导力水平,其中每个也是 一定的。
写一个程序,给定每忍者 写一个程序,给定每忍者 i的上级 Bi,薪水 ,薪水 Ci,领导力 Li,以及支付给 ,以及支付给 ,以及支付给 忍者们的薪水总预算 M,输出在预算内 满足上述要求 时顾客 满意度的最大值。
【数据范围】
1 ≤ N ≤ 100,000 忍者的个数 ;
1 ≤ M ≤ 1,000,000,000 薪水总预算 ;
0 ≤ Bi < i< i 忍者的 上级 的编号;
1 ≤ Ci ≤ M 忍者的薪水 ;
1 ≤ Li ≤ 1,000,000,000 忍者的领导力水平 。
对于 30% 的数据, N ≤ 3000 。
【输入格式】
从标准输入读数据 。
第一行 包含 两个整数 N和 M,其中 N表示忍者的个数, 表示忍者的个数, M表示薪水的总预 算。
接下来 N行描述忍者们的 上级、 薪水以及领导力。 其中的 第 i行包含三个整 数 Bi , Ci , Li分别表示第 分别表示第 i个忍者的 上级 ,薪水以及领导力。 Master Master满足 Bi = 0, 并且每一个忍者的老板编号 定小于自己Bi < i。
【输出格式】
输出 到标准。
输出 一个数,表示 在【样例输入】
5 4
0 3
1 3 5
2
1 2 4
2 3 1
【样例输出】
6
【样例说明】
如果我们选择 编号为 1的忍者作为管理并且派遣第三个和四,薪 忍者作为管理并且派遣第三个和四,薪 水总和为 4,没有超过总预算 ,没有超过总预算 4。因为派遣了 。因为派遣了 2个忍者并且管理的领导力为 3, 用户的满意度为 2 × 3 = 6 3 = 6 ,是可以得到的用户 满意度 的
题解:
看了题目大概知道就是一棵树,我们分别搜索树中每一个节点为根节点所形成的子树,在每一棵子树中找到薪水之和少于预算的最多人数,使该人数与该节点的领导能力值乘积最小化。
可以用平衡树+启发式合并来做。
所谓的启发式合并就是当你合并两颗平衡树的时候,将节点少的平衡树每个节点拆开来,插入节点多的那颗平衡树上。
我们将每个树中每个节点都建立一颗splay,然后差不多按照树形DP的思想,将某一个节点的所有子节点的splay都插入到该节点的splay,然后维护,并找出该节点splay中薪水之和少于预算的最多人数,更新一下答案即可。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; const int maxn = 100001; int L[maxn],n,m; struct edgeNode{ int v; edgeNode *next; }*g[maxn]; void insert(int a,int b){ static edgeNode buf[maxn]; static int top = 0; edgeNode *p = &buf[top++]; p->v = b;p->next = g[a];g[a] = p; } struct splayNode{ int key,size; long long sum; splayNode *pre,*ch[2]; void push_up(){ size = ch[0]->size + ch[1]->size + 1; sum = ch[0]->sum + ch[1]->sum + key; } }*root[maxn],*null,*queue[maxn],*tt; splayNode *newNode(int value){ static splayNode buf[maxn]; static int top = 0; splayNode *p = &buf[top++]; p->key = p->sum = value;p->size = 1; p->ch[0] = p->ch[1] = p->pre = null; return p; } void init(){ null = newNode(0); null->size = 0; } void rotate(splayNode *x,int c){ splayNode *y = x->pre; y->ch[!c] = x->ch[c]; if (y->ch[!c] != null) y->ch[!c]->pre = y; x->pre = y->pre; if (x->pre != null) x->pre->ch[y == x->pre->ch[1]] = x; x->ch[c] = y; y->pre = x; y->push_up(); } void splay(splayNode *x){ while (x->pre != null){ if (x->pre->pre == null){ rotate(x,x == x->pre->ch[0]); break; } splayNode *y = x->pre,*z = y->pre; int f = (y == z->ch[0]); if (x == y->ch[f]) rotate(x,!f),rotate(x,f); else rotate(y,f),rotate(x,f); } x->push_up(); } void insert(splayNode *obj,splayNode* &pre){ splayNode *t = pre; while (true){ if (obj->key < t->key){ if (t->ch[0] == null){ t->ch[0] = obj; obj->pre = t; break; }else t = t->ch[0]; }else{ if (t->ch[1] == null){ t->ch[1] = obj; obj->pre = t; break; }else t = t->ch[1]; } } splay(obj); pre = obj;//这点很重要,白天就是这个萎掉了。 } splayNode *merge(splayNode *x,splayNode *y){ if (x->size < y->size){swap(x,y);} queue[1] = y;int head = 0,tail = 1; while (head<tail){ splayNode *t = queue[++head]; if (t->ch[0] != null) queue[++tail] = t->ch[0]; if (t->ch[1] != null) queue[++tail] = t->ch[1]; t->size = 1;t->sum = t->key; t->pre = t->ch[0] = t->ch[1] = null; insert(t,x); }return x; } int maxNum(splayNode *x,long long limit){ if (x== null) return 0; long long tmp = x->ch[0]->sum + x->key; if (x->ch[0]->sum<=limit && tmp>limit) return x->ch[0]->size; if (tmp>limit) return maxNum(x->ch[0],limit); else return maxNum(x->ch[1],limit-tmp) + x->ch[0]->size + 1; } void print(splayNode *x){ if (x->ch[0] !=null) print(x->ch[0]); cout << x->key << endl; if (x->ch[1] !=null) print(x->ch[1]); } long long solve(int i,int m){ long long res = 0; edgeNode *t = g[i]; while (t){ res = max(res,solve(t->v,m)); root[i] = merge(root[i],root[t->v]); t = t->next; } res = max(res,(long long)L[i] * maxNum(root[i],m)); return res; } int main(){ freopen("dispatching.in","r",stdin); freopen("dispatching.out","w",stdout); scanf("%d %d\n",&n,&m);init(); memset(g,0,sizeof(g)); for (int i=1,b,c;i<=n;++i){ scanf("%d %d %d\n",&b,&c,&L[i]); if (b>0) insert(b,i); root[i] = newNode(c); } long long ans = solve(1,m); printf("%lld\n",ans); }
NOI2003:EDITOR
题目详见点击打开链接
不过我感觉样例第5行有错误,16应该为15把
题目大意就是维护一个字符组成的序列,进行插入删除输出操作,经过前面的训练,这就是裸的模板题了。。。
#include <iostream> #include <cstdio> #include <cstdlib> #define maxn 1024*1024*2+10 #define INF 0x7FFFFFFF #define keyTree root->ch[1]->ch[0] using namespace std; int t,cursor,a[maxn]; struct node{ int key,size; node *pre,*ch[2]; inline void push_up(){ size = ch[0]->size + ch[1]->size + 1; } }; class splayTree{ public: node *root,*null; node buf[maxn]; int top,n; inline void init(){ null = newNode(-INF);null->size = 0; root = newNode(-INF); root->ch[1] = newNode(-INF);root->ch[1]->pre = root; } inline node *newNode(int value){ node *x = &buf[top++]; x->key = value;x->size = 1; x->pre = x->ch[0] = x->ch[1] = null; return x; } inline node *build(int l,int r){ if (l>r) return null; int mid = (l+r) >> 1; node *x = newNode(a[mid]); x->ch[0] = build(l,mid-1); x->ch[1] = build(mid+1,r); if (x->ch[0] != null) x->ch[0]->pre = x; if (x->ch[1] != null) x->ch[1]->pre = x; x->push_up(); return x; } inline void rotate(node *x,int c){ node *y = x->pre; y->ch[!c] = x->ch[c]; if (y->ch[!c] != null) y->ch[!c]->pre = y; x->pre = y->pre; if (x->pre != null) x->pre->ch[y == x->pre->ch[1]] = x; x->ch[c] = y; y->pre = x; if (y == root) root = x; y->push_up(); } inline void splay(node *x,node *g){ while (x->pre != g){ if (x->pre->pre == g){ rotate(x,x == x->pre->ch[0]);break; } node *y = x->pre,*z = y->pre; int f = (y == z->ch[0]); if (x == y->ch[f]) rotate(x,!f),rotate(x,f); else rotate(y,f),rotate(x,f); } x->push_up(); } inline void select(node *x,int k){ node *t = root; while (true){ int tmp = t->ch[0]->size; if (tmp == k) break; if (tmp < k) k-=tmp+1,t = t->ch[1]; else t = t->ch[0]; } splay(t,x); } inline void insert(){ scanf("%d",&n); for (int i=1;i<=n;++i){ char c; scanf("%c",&c); while (c == '\n') scanf("%c",&c); a[i] = c; } select(null,cursor); select(root,cursor+1); keyTree = build(1,n); keyTree->pre = root->ch[1]; splay(keyTree,null); } inline void del(){ scanf("%d",&n); select(null,cursor); select(root,cursor+n+1); keyTree = null; root->ch[1]->push_up(); root->push_up(); splay(root->ch[1],null); } inline void get(){ scanf("%d",&n); select(null,cursor); select(root,cursor+n+1); print(keyTree); printf("\n"); splay(keyTree,null); } inline void print(node *x){ if (x->ch[0] != null) print(x->ch[0]); if (x->key != -INF) printf("%c",x->key); if (x->ch[1] != null) print(x->ch[1]); } }spt; int main(){ freopen("editor.in","r",stdin); freopen("editor.out","w",stdout); scanf("%d",&t); spt.init(); char op[30]; for (int i=1;i<=t;++i){ scanf("%s",op); switch (op[0]){ case 'I':spt.insert();break; case 'M':scanf("%d",&cursor);break; case 'D':spt.del();break; case 'G':spt.get();break; case 'P':cursor--;break; case 'N':cursor++;break; } } }
题目详见点击打开链接
这道题目要我们输出两个值,一个是sum值(表示某一段的和),一个是max_sum(表示数列中最长的子段和),以及支持4中操作,在某一个位置插入一段数,在某一个位置开始删除一段数,翻转某一段数,把某一段数全改为同一个数。
我们需要开8个变量:key,maxL,maxR,maxSum,sum,same,size,rev;
key用来保存当前节点的值。
maxL表示当前节点表示的区间的左端最大子段和。
maxR表示当前节点表示的区间的右端最大子段和。
maxSum表示当前节点表示的区间的最大子段和。
sum表示当前节点表示的区间的总和。
same表示当前节点表示的区间是否被改为同一个数,如果是,那么same就表示被改成的那个数
size表示当前节点表示的区间的长度
rev表示当前节点表示的区间是否被翻转
根据平衡树维护数列+线段树打标记的操作原理,就可以了。
#include <iostream> #include <cstdio> #include <cstdlib> #define maxn 500000 #define INF 1001 #define keyTree root->ch[1]->ch[0] using namespace std; int n,m,a[maxn]; struct node{ int key,maxL,maxR,maxSum,sum,same,size; bool rev; node *pre,*ch[2]; inline void reverse(){ if (size == 0) return; rev ^= 1; swap(ch[0],ch[1]); swap(maxL,maxR); } inline void saming(int x){ if (size == 0) return; key = same = x; maxL = maxR = maxSum = sum = x*size; if (x<0) maxL = maxR = maxSum = x; } inline void push_up(){ sum = ch[0]->sum+ch[1]->sum+key; size = ch[0]->size + ch[1]->size + 1; maxL = max(ch[0]->maxL,max(ch[0]->sum+key,ch[0]->sum+key+ch[1]->maxL)); maxR = max(ch[1]->maxR,max(ch[1]->sum+key,ch[1]->sum+key+ch[0]->maxR)); maxSum = max(ch[0]->maxSum,max(ch[1]->maxSum,max(ch[0]->maxR+key,max(ch[1]->maxL+key,max(ch[0]->maxR+key+ch[1]->maxL,key))))); } inline void push_down(){ if (rev){ ch[0]->reverse(); ch[1]->reverse(); }rev = 0; if (same!=INF){ ch[0]->saming(same); ch[1]->saming(same); }same = INF; } }; class splayTree{ public: node *root,*null,*stk[maxn]; node buf[maxn]; int top,cnt,num; int pos,tot,c,pop; inline void erase(node *x){ x->size = x->sum = x->maxL = x->maxR = x->maxSum = 0; } inline node *newNode(int value){ node *x; if (cnt) x = stk[cnt--]; else x = &buf[top++]; x->key = x->maxL = x->maxR = x->maxSum = x->sum = value; x->size = 1,x->rev = 0; x->pre = x->ch[0] = x->ch[1] = null; x->same = INF; return x; } inline void init(){ top = cnt = 0;num = n; null = newNode(-INF);null->size = 0,null->sum = 0; root = newNode(-INF);root->sum = 0; root->ch[1] = newNode(-INF);root->ch[1]->pre = root;root->ch[1]->sum =0; } inline node *build(int l,int r){ if (l>r) return null; int mid = (l+r) >> 1; node *x = newNode(a[mid]); x->ch[0] = build(l,mid-1); x->ch[1] = build(mid+1,r); if (x->ch[0] != null) x->ch[0]->pre = x; if (x->ch[1] != null) x->ch[1]->pre = x; x->push_up(); return x; } inline void rotate(node *x,int c){ node *y = x->pre; y->push_down();x->push_down(); y->ch[!c] = x->ch[c]; if (y->ch[!c] != null) y->ch[!c]->pre = y; x->pre = y->pre; if (x->pre != null) x->pre->ch[y == x->pre->ch[1]] = x; x->ch[c] = y; y->pre = x; if (y == root) root = x; y->push_up(); } inline void splay(node *x,node *g){ x->push_down(); while (x->pre != g){ if (x->pre->pre == g){ rotate(x,x == x->pre->ch[0]);break; } node *y = x->pre,*z = y->pre; int f = (y == z->ch[0]); if (x == y->ch[f]) rotate(x,!f),rotate(x,f); else rotate(y,f),rotate(x,f); } x->push_up(); } inline void select(node *x,int k){ node *t = root; while (true){ t->push_down(); int tmp = t->ch[0]->size; if (tmp == k) break; if (tmp < k) k-= tmp+1,t = t->ch[1]; else t = t->ch[0]; } splay(t,x); } inline void recycle(node *x){ if (x->ch[0] != null) recycle(x->ch[0]); stk[++cnt] = x; if (x->ch[1] != null) recycle(x->ch[1]); } inline void insert(){ scanf("%d%d",&pos,&tot); num+= tot; for (int i = 1;i<=tot;++i) scanf("%d",&a[i]); select(null,pos); select(root,pos+1); keyTree = build(1,tot); keyTree->pre = root->ch[1]; splay(keyTree,null); } inline void del(){ scanf("%d%d",&pos,&tot); select(null,pos-1); select(root,pos+tot); if (keyTree != null){ num-= keyTree->size; recycle(keyTree); root->ch[1]->ch[0] = null; root->ch[1]->push_up(); root->push_up(); } splay(root->ch[1],null); } inline void make_same(){ scanf("%d%d%d",&pos,&tot,&c); select(null,pos-1); select(root,pos+tot); if (keyTree != null){ keyTree->saming(c); splay(keyTree,null); } } inline void reverse(){ scanf("%d%d",&pos,&tot); select(null,pos-1); select(root,pos+tot); if (keyTree != null){ keyTree->reverse(); splay(keyTree,null); } } inline void max_sum(){ printf("%d\n",root->maxSum); } inline void get_sum(){ scanf("%d%d",&pos,&tot); select(null,pos-1); select(root,pos+tot); if (keyTree!= null){ printf("%d\n",keyTree->sum); splay(keyTree,null); keyTree->push_down(); } else printf("0\n"); } }spt; int main(){ freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); scanf("%d %d\n",&n,&m); for (int i=1;i<=n;++i) scanf("%d",&a[i]); spt.init(); spt.keyTree = spt.build(1,n); spt.keyTree->pre = spt.root->ch[1]; spt.splay(spt.keyTree,spt.null); char op[30]; for (int i=1;i<=m;++i){ scanf("%s",op); switch (op[0]){ case 'I':spt.insert();break; case 'D':spt.del();break; case 'R':spt.reverse();break; case 'G':spt.get_sum();break; case 'S':spt.make_same();break; case 'M':if (op[2] == 'X') spt.max_sum();else spt.make_same();break; } } }