这个题集好好,那天我做一下(开个大坑?)
嘛,题目难的是查询,这里就只讲查询了
根据题解,我们用线段树维护3个东西
lx:表示从lx走,所能在给定区间里面的最大值
rx:参考上面
mx:表示在给定区间中间截取一段所能取到的最大值
那么对于题目给定的查询区间(假设其为L,R)
我们用线段树去遍历这个区间(访问,,,,,比较好点吧)
如果L---R刚好对应线段树的一个区间,那么直接返回我们的mx就好了(正确性显然)
相对的,存在不对应区间的情况
考虑合并答案
对于我们线段树的每次操作相当于把一段区间一分为二
那么我们可以借用线段树的操作,维护一个在线段树维护范围内的又在L---R的部分答案
然后就可以合并
对于这个部分答案,很好维护
只要将遍历所有L---R内的线段树局部节点就可以解决
#include<bits/stdc++.h> #define MAXN 400000 using namespace std; int n,q,date[MAXN]; int tp,mb1,mb2; struct node{ int sum; int lx,rx,mx; }t[MAXN*4]; int up(int now){ t[now].sum = t[now*2].sum+t[now*2+1].sum; t[now].lx = max(t[now*2].lx,t[now*2].sum+t[now*2+1].lx); t[now].rx = max(t[now*2+1].rx,t[now*2].rx+t[now*2+1].sum); t[now].mx = max(max(t[now*2].mx,t[now*2+1].mx),t[now*2].rx+t[now*2+1].lx); } int build(int ll,int rr,int now){ if(ll==rr){ t[now].lx = t[now].rx = t[now].mx = t[now].sum = date[ll]; return 0; } int mid = (ll+rr)>>1; build(ll,mid,now*2); build(mid+1,rr,now*2+1); up(now); } int change(int now,int l,int r,int x,int y){//在x上改成y int mid = (l+r)>>1; if(r<x||l>x)return 0; if(x==l&&x==r){ t[now].lx = t[now].rx = t[now].mx = t[now].sum = y; return 0; } change(now*2+1,mid+1,r,x,y); change(now*2,l,mid,x,y); up(now); } node found(int ml,int mr,int now,int l,int r){ if(l>=ml&&r<=mr){ return t[now]; } int mid = (l+r)>>1; node ans,a,b; if(mid<ml)return found(ml,mr,now*2+1,mid+1,r); if(mid>=mr)return found(ml,mr,now*2,l,mid); else{ a = found(ml,mr,now*2,l,mid); b = found(ml,mr,now*2+1,mid+1,r); ans.sum = a.sum+b.sum; ans.lx = max(a.lx,a.sum+b.lx); ans.rx = max(a.rx+b.sum,b.rx); ans.mx = max(a.rx+b.lx,max(a.mx,b.mx)); return ans; } } int main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>date[i]; } build(1,n,1); cin>>q; for(int i=1;i<=q;i++){ cin>>tp>>mb1>>mb2; if(tp==1){ node p=found(mb1,mb2,1,1,n); cout<<p.mx<<endl; } else{ change(1,1,n,mb1,mb2); } } }
当然,也可以用平衡树解决此问题(嘛,平衡树完全不会做....)
一般的,假设平衡树已经维护了原区间序列的编号
只要使L-1作为根节点,R+1作为根右节点,此时因为平衡树同时满足BST性质
故R的左子树就是L---R区间的编号(一开始搞懵了)
本质上在平衡树上对原序列的操作是不需要遵守BST性质的
因为与编号无关,只与编号映射的集合有关
那么考虑 提取区间后的操作....
同样的参照线段树做法...维护3个东西lx,rx,mx
(此时开始乱搞....)
嘛,直接仿造线段树的插入方式,但维护的不是区间是节点....
然后考虑旋转怎么维护那3坨东西
嘛,考虑到变的只有旋转点和旋转点父亲
直接up一下就好
-----------------------------------蒟蒻分割线
不过还好,让我重温线段树....
嘛....
splay的代码...
#include<bits/stdc++.h> #define MAXN 100005 using namespace std; int n,q,ma,mb,tp; int date[MAXN]; int lx[MAXN],rx[MAXN],mx[MAXN],sum[MAXN]; struct Splay{ int num[MAXN],ch[MAXN][2],sz[MAXN],f[MAXN],cnt[MAXN],rt,tot,tag[MAXN]; int get(int x){ return (x==ch[f[x]][1]); } int up(int x){ sz[x] = cnt[x]; if(ch[x][0])sz[x] = sz[x]+sz[ch[x][0]]; if(ch[x][1])sz[x] = sz[x]+sz[ch[x][1]]; sum[x] = date[x]+sum[ch[x][0]]+sum[ch[x][1]]; lx[x] = max(lx[ch[x][0]],lx[ch[x][1]]+sum[ch[x][0]]+date[x]); rx[x] = max(rx[ch[x][1]],rx[ch[x][0]]+sum[ch[x][1]]+date[x]); mx[x] = max(max(mx[ch[x][0]],mx[ch[x][1]]),date[x]+rx[ch[x][0]]+lx[ch[x][1]]); return 0; } int down(int x){ if(tag[x]){ tag[x] = 0; swap(ch[x][0],ch[x][1]); if(ch[x][0])tag[ch[x][0]] = tag[ch[x][0]]^1; if(ch[x][1])tag[ch[x][1]] = tag[ch[x][1]]^1; } } int found(int x){ int now=rt,ans=1; while(1){ if(num[now]<=x){ ans = ans+sz[ch[now][0]]; if(num[now]==x){ return ans; } ans = ans+cnt[now]; now = ch[now][1]; } else{ now = ch[now][0]; } } } int rote(int x){ int y = f[x],z = f[y],k = get(x),p = get(y); down(y),down(x); if(y==0)return 0; if(z)ch[z][p] = x; if(ch[x][k^1])f[ch[x][k^1]] = y; ch[y][k] = ch[x][k^1]; ch[x][k^1] = y; f[x] = z; f[y] = x; up(y);up(x); return 0; } int splay(int x){ for(int y = f[x];(y = f[x]);rote(x)){ rote((get(x)==get(y))?y:x); } rt = x; return 0; } int rank(int x){ int now=rt,ans=1; while(1){ if(num[now]<=x){ ans = ans+sz[ch[now][0]]; if(num[now]==x){ splay(now); return ans; } ans = ans+cnt[now]; now = ch[now][1]; } else{ now = ch[now][0]; } } } int pre(){//前继 比她小的最大 int now = ch[rt][0]; while(ch[now][1])now = ch[now][1]; return now; } int suc(){ int now = ch[rt][1]; while(ch[now][0])now = ch[now][0]; return now; } int add(int x){ int now = rt; while(1){ down(now); if(num[now]==x){ cnt[now]++; splay(now); return 0; } int to = (num[now]<x); if(!ch[now][to]){ tot++; ch[now][to] = tot; num[tot] = x; f[tot] = now; cnt[tot] = 1; sz[tot] = 1; splay(tot); return 0; } else now = ch[now][to]; } } int kth(int x){ int now =rt; while(1){ down(now); if(sz[ch[now][0]]<x){ x = x-sz[ch[now][0]]; if(x<=cnt[now])return num[now]; x = x-cnt[now]; now = ch[now][1]; } else{ now = ch[now][0]; } } } int del(int x){ rank(x); if(cnt[rt]>=2){ cnt[rt]--; sz[rt]--; return 0; } else{ if((!ch[rt][0])&&(!ch[rt][1])){ rt = 0; return 0; } if(ch[rt][0]&&(!ch[rt][1])){ f[ch[rt][0]] = 0; rt =ch[rt][0]; return 0; } if((!ch[rt][0])&&ch[rt][1]){ f[ch[rt][1]] = 0; rt =ch[rt][1]; return 0; } else{ splay(pre()); ch[rt][1] = ch[ch[rt][1]][1]; f[ch[rt][1]] = rt; up(rt); return 0; } } } int splay2(int x){ for(int y = f[x];(y = f[x])!=rt;rote(x)){ if(f[y]!=rt)rote((get(x)==get(y))?y:x); } return 0; } int turn(int x,int g){ int p=kth(x-1),k=kth(g+1); splay(p); splay2(k); tag[ch[ch[rt][1]][0]] = 1^tag[ch[ch[rt][1]][0]]; } int change(int x,int g){ int p=kth(x-1),k=kth(x+1); splay(p); splay2(k); date[ch[ch[rt][1]][0]] = g; lx[ch[ch[rt][1]][0]] = rx[ch[ch[rt][1]][0]] = max(g,0); sum[ch[ch[rt][1]][0]] = mx[ch[ch[rt][1]][0]] = g; up(ch[rt][1]); up(rt); } int found(int x,int g){ int p=kth(x-1),k=kth(g+1); splay(p); splay2(k); cout<<mx[ch[ch[rt][1]][0]]<<endl; } int print(int now){ down(now); if(ch[now][0])print(ch[now][0]); if(num[now]>1&&num[now]<n+2)cout<<num[now]-1<<" "; if(ch[now][1])print(ch[now][1]); } }T; int main(){ //freopen("out.txt","r",stdin); cin>>n; mx[0] = date[1] = date[2] =-1e9;//一定要设置 for(int i=2;i<=n+1;i++){ cin>>date[i]; //lx[i] = rx[i] = max(date[i],0); //sum[i] = mx[i] = date[i]; } for(int i=1;i<=n+2;i++)T.add(i); cin>>q; for(int i=1;i<=q;i++){ cin>>tp>>ma>>mb; if(tp==0){ T.change(ma+1,mb); } else{ T.found(ma+1,mb+1); } } }
抄模板真香(是不是该学分快了..)