众所周知的仅次于ynoi的毒瘤数据结构系列。(跟Qtree系列并列?)


GSS1:

长度为 $n$ 的序列 $a$,$m$ 个询问,每次询问区间 $[l,r]$ 之间的最大子段和。

$1\le n,m\le 5\times 10^4$。

经典的线段树题。

每个节点维护四个值:$sum,lmax,rmax,amax$。

$sum$ 表示整个区间的和。

$lmax$ 表示以 $l$ 为左端点的最大子段和。

$rmax$ 表示以 $r$ 为右端点的最大子段和。

$amax$ 表示整个的最大子段和。

时间复杂度 $O(m\log n)$。

具体可以看代码。(那是好久以前写的了,所以会有点丑)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int maxm=;
  4. namespace Segment_Tree{
  5. struct node{
  6. int sum,lmax,rmax,amax;
  7. }e[maxm];
  8. int n;
  9. inline void pushup(int t){
  10. node ls=e[t<<],rs=e[t<<|];
  11. e[t]=(node){
  12. ls.sum+rs.sum,
  13. max(ls.lmax,ls.sum+rs.lmax),
  14. max(rs.rmax,rs.sum+ls.rmax),
  15. max(max(ls.amax,rs.amax),ls.rmax+rs.lmax)
  16. };
  17. }
  18. void build_(int l,int r,int t){
  19. if(l==r){
  20. scanf("%d",&e[t].sum);
  21. e[t]=(node){e[t].sum,e[t].sum,e[t].sum,e[t].sum};
  22. return;
  23. }
  24. int mid=l+r>>;
  25. build_(l,mid,t<<);
  26. build_(mid+,r,t<<|);
  27. pushup(t);
  28. }
  29. void build(int x){
  30. n=x;
  31. build_(,x,);
  32. }
  33. node query_(int L,int R,int l,int r,int t){
  34. if(L>=l && R<=r) return e[t];
  35. int mid=L+R>>;
  36. if(mid>=r) return query_(L,mid,l,r,t<<);
  37. if(mid<l) return query_(mid+,R,l,r,t<<|);
  38. node ans,ls,rs;
  39. ls=query_(L,mid,l,r,t<<);rs=query_(mid+,R,l,r,t<<|);
  40. ans.sum=ls.sum+rs.sum;
  41. ans.lmax=max(ls.lmax,ls.sum+rs.lmax);
  42. ans.rmax=max(rs.rmax,rs.sum+ls.rmax);
  43. ans.amax=max(max(ls.amax,rs.amax),ls.rmax+rs.lmax);
  44. return ans;
  45. }
  46. int query(int l,int r){
  47. return query_(,n,l,r,).amax;
  48. }
  49. }
  50. int n,m;
  51. int main(){
  52. scanf("%d",&n);
  53. Segment_Tree::build(n);
  54. scanf("%d",&m);
  55. for(int i=;i<=m;i++){
  56. int l,r;
  57. scanf("%d%d",&l,&r);
  58. printf("%d\n",Segment_Tree::query(l,r));
  59. }
  60. }

GSS1


GSS2:

长度为 $n$ 的序列 $a$,$m$ 个询问,每次询问区间 $[l,r]$ 之间的最大子段和。可以选空子段。注意此处的子段和是:如果一个数出现了多次,那么只算一次。

$1\le n,m\le 10^5$。

这就摇身一变变成了毒瘤题……ErkkiErkko Orz……(这是他洛谷账号)

一个数只算一次,有点像HH的项链,那么也可以用类似的想法。

把询问离线,按右端点排序。

假设现在推右端点推到了 $i$。

线段树维护的东西得修改一下。(具体原因待会解释)

每个点 $sum,hmax$。标记 $tag,htag$。

对于叶子结点 $l$,$sum$ 表示从 $l$ 到 $i$ 的和(也是相同的数只算一次),$hmax$ 表示这个节点 $sum$ 曾达到过的最大值,初始为 $0$。

对于非叶子结点,$sum$ 是所有孩子的 $sum$ 的最大值,$hmax$ 是所有孩子的 $hmax$ 的最大值。

$tag$ 是因为下面需要区间加的标记,$htag$ 是自上次把这个节点的标记下传完以后,$tag$ 达到过的最大值。

有点不好说啊……

那么 $pre[a[i]]$(就是 $a[i]$ 上一次出现的位置)和之前的位置到 $i$ 的和应该和到 $i-1$ 的和一样不变,因为前面已经出现过 $a[i]$,现在这个 $a[i]$ 就不能再算一遍。

$pre[a[i]]$ 之后的到 $i-1$ 的和由于没有出现过 $a[i]$,所以他们的和就要加 $a[i]$。即给 $sum$ 区间加 $a[i]$。

接下来查询就好办了,就是这个区间 $hmax$ 的最大值。(想一想,为什么?)

时间复杂度 $O(m(\log n+\log m))$。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const int maxn=;
  5. #define lson o<<1,l,mid
  6. #define rson o<<1|1,mid+1,r
  7. #define FOR(i,a,b) for(int i=(a);i<=(b);i++)
  8. #define ROF(i,a,b) for(int i=(a);i>=(b);i--)
  9. #define MEM(x,v) memset(x,v,sizeof(x))
  10. inline int read(){
  11. char ch=getchar();int x=,f=;
  12. while(ch<'' || ch>'') f|=ch=='-',ch=getchar();
  13. while(ch>='' && ch<='') x=x*+ch-'',ch=getchar();
  14. return f?-x:x;
  15. }
  16. struct query{
  17. int l,r,id;
  18. bool operator<(const query &q)const{
  19. return r<q.r;
  20. }
  21. }qqq[maxn];
  22. int n,q,pre[maxn*],a[maxn];
  23. ll ans[maxn],sum[maxn*],hmax[maxn*],tag[maxn*],htag[maxn*];
  24. void pushup(int o){
  25. sum[o]=max(sum[o<<],sum[o<<|]);
  26. hmax[o]=max(hmax[o<<],hmax[o<<|]);
  27. }
  28. void pushdown(int o){
  29. hmax[o<<]=max(hmax[o<<],sum[o<<]+htag[o]);
  30. hmax[o<<|]=max(hmax[o<<|],sum[o<<|]+htag[o]);
  31. sum[o<<]+=tag[o];
  32. sum[o<<|]+=tag[o];
  33. htag[o<<]=max(htag[o<<],tag[o<<]+htag[o]);
  34. htag[o<<|]=max(htag[o<<|],tag[o<<|]+htag[o]);
  35. tag[o<<]+=tag[o];
  36. tag[o<<|]+=tag[o];
  37. tag[o]=htag[o]=;
  38. }
  39. void update(int o,int l,int r,int ql,int qr,int x){
  40. if(l>=ql && r<=qr){
  41. sum[o]+=x;
  42. hmax[o]=max(hmax[o],sum[o]);
  43. tag[o]+=x;
  44. htag[o]=max(htag[o],tag[o]);
  45. return;
  46. }
  47. pushdown(o);
  48. int mid=(l+r)>>;
  49. if(mid>=ql) update(lson,ql,qr,x);
  50. if(mid<qr) update(rson,ql,qr,x);
  51. pushup(o);
  52. }
  53. ll query(int o,int l,int r,int ql,int qr){
  54. if(l>=ql && r<=qr) return hmax[o];
  55. pushdown(o);
  56. int mid=(l+r)>>;ll ans=-1e18;
  57. if(mid>=ql) ans=max(ans,query(lson,ql,qr));
  58. if(mid<qr) ans=max(ans,query(rson,ql,qr));
  59. return ans;
  60. }
  61. int main(){
  62. n=read();
  63. FOR(i,,n) a[i]=read();
  64. q=read();
  65. FOR(i,,q) qqq[i].l=read(),qqq[i].r=read(),qqq[i].id=i;
  66. sort(qqq+,qqq+q+);
  67. int curr=;
  68. FOR(i,,q){
  69. while(curr<=qqq[i].r){
  70. update(,,n,pre[a[curr]+]+,curr,a[curr]);
  71. pre[a[curr]+]=curr;
  72. curr++;
  73. }
  74. ans[qqq[i].id]=query(,,n,qqq[i].l,qqq[i].r);
  75. }
  76. FOR(i,,q) printf("%lld\n",ans[i]);
  77. }

GSS2


GSS3:

长度为 $n$ 的序列 $a$,$m$ 个操作:单点修改,或询问区间的最大子段和。

$1\le n,m\le 5\times 10^4$。

跟GSS1一样,不说了。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int maxn=;
  4. #define lson o<<1,l,mid
  5. #define rson o<<1|1,mid+1,r
  6. #define FOR(i,a,b) for(int i=(a);i<=(b);i++)
  7. #define ROF(i,a,b) for(int i=(a);i>=(b);i--)
  8. #define MEM(x,v) memset(x,v,sizeof(x))
  9. inline int read(){
  10. char ch=getchar();int x=,f=;
  11. while(ch<'' || ch>'') f|=ch=='-',ch=getchar();
  12. while(ch>='' && ch<='') x=x*+ch-'',ch=getchar();
  13. return f?-x:x;
  14. }
  15. struct node{
  16. int sum,lmax,rmax,amax;
  17. }nd[maxn*];
  18. int n,q,a[maxn];
  19. void pushup(node &o,node l,node r){
  20. o.sum=l.sum+r.sum;
  21. o.lmax=max(l.lmax,l.sum+r.lmax);
  22. o.rmax=max(r.rmax,r.sum+l.rmax);
  23. o.amax=max(l.amax,max(r.amax,l.rmax+r.lmax));
  24. }
  25. void build(int o,int l,int r){
  26. if(l==r) return void(nd[o]=(node){a[l],a[l],a[l],a[l]});
  27. int mid=(l+r)>>;
  28. build(lson);
  29. build(rson);
  30. pushup(nd[o],nd[o<<],nd[o<<|]);
  31. }
  32. void update(int o,int l,int r,int p,int x){
  33. if(l==r) return void(nd[o]=(node){x,x,x,x});
  34. int mid=(l+r)>>;
  35. if(mid>=p) update(lson,p,x);
  36. else update(rson,p,x);
  37. pushup(nd[o],nd[o<<],nd[o<<|]);
  38. }
  39. node query(int o,int l,int r,int ql,int qr){
  40. if(l>=ql && r<=qr) return nd[o];
  41. int mid=(l+r)>>;
  42. if(mid<ql) return query(rson,ql,qr);
  43. if(mid>=qr) return query(lson,ql,qr);
  44. node ans;
  45. pushup(ans,query(lson,ql,qr),query(rson,ql,qr));
  46. return ans;
  47. }
  48. int main(){
  49. n=read();
  50. FOR(i,,n) a[i]=read();
  51. build(,,n);
  52. q=read();
  53. FOR(i,,q){
  54. int op=read(),l=read(),r=read();
  55. if(op) printf("%d\n",query(,,n,l,r).amax);
  56. else update(,,n,l,r);
  57. }
  58. }

GSS3


GSS4:

长度为 $n$ 的正整数序列 $a$,$m$ 个操作:区间开方(下取整),区间求和。

$1\le n,m\le 10^5,\sum a_i\le 10^{18}$。

这应该也是一个套路了……

我们发现一个数 $x$ 开方下取整约 $\log\log x$ 次之后便会变成 $1$。变成 $1$ 之后,再开方都不会对答案有影响。

(你问我为什么是 $\log \log x$?因为每开一次方实际上就是把 $\log x$ 除以 $2$,这样操作一共就要 $\log \log x$ 次)

那么就上线段树,维护区间和和区间是否都为 $1$(bool)。

修改时,对每个叶子结点都暴力修改。注意的是如果到了一个区间,区间都是 $1$,就可以跳过这个区间,因为修不修改不影响答案。

由于一个叶子至多被暴力 $\log\log a_i$ 次,所以时间复杂度是 $O(q\log n\log\log a_i)$。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const int maxn=;
  5. #define lson o<<1,l,mid
  6. #define rson o<<1|1,mid+1,r
  7. #define FOR(i,a,b) for(int i=(a);i<=(b);i++)
  8. #define ROF(i,a,b) for(int i=(a);i>=(b);i--)
  9. #define MEM(x,v) memset(x,v,sizeof(x))
  10. inline ll read(){
  11. char ch=getchar();ll x=,f=;
  12. while(ch<'' || ch>'') f|=ch=='-',ch=getchar();
  13. while(ch>='' && ch<='') x=x*+ch-'',ch=getchar();
  14. return f?-x:x;
  15. }
  16. int t,n,q;
  17. ll a[maxn],sum[maxn*];
  18. bool all1[maxn*];
  19. inline void pushup(int o){
  20. sum[o]=sum[o<<]+sum[o<<|];
  21. all1[o]=all1[o<<]&all1[o<<|];
  22. }
  23. void build(int o,int l,int r){
  24. if(l==r) return void((sum[o]=a[l],all1[o]=(a[l]==)));
  25. int mid=(l+r)>>;
  26. build(lson);
  27. build(rson);
  28. pushup(o);
  29. }
  30. void update(int o,int l,int r,int ql,int qr){
  31. if(all1[o]) return;
  32. if(l==r) return void((sum[o]=sqrt(sum[o]),all1[o]=(sum[o]==)));
  33. int mid=(l+r)>>;
  34. if(mid>=ql) update(lson,ql,qr);
  35. if(mid<qr) update(rson,ql,qr);
  36. pushup(o);
  37. }
  38. ll query(int o,int l,int r,int ql,int qr){
  39. if(l>=ql && r<=qr) return sum[o];
  40. int mid=(l+r)>>;ll s=;
  41. if(mid>=ql) s+=query(lson,ql,qr);
  42. if(mid<qr) s+=query(rson,ql,qr);
  43. return s;
  44. }
  45. int main(){
  46. while(~scanf("%d",&n)){
  47. printf("Case #%d:\n",++t);
  48. FOR(i,,n) a[i]=read();
  49. build(,,n);
  50. q=read();
  51. FOR(i,,q){
  52. int op=read(),l=read(),r=read();
  53. if(l>r) swap(l,r);
  54. if(op) printf("%lld\n",query(,,n,l,r));
  55. else update(,,n,l,r);
  56. }
  57. puts("");
  58. }
  59. }

GSS4


GSS5:

长度为 $n$ 的正整数序列 $a$,$m$ 个询问,每次询问左端点在 $[x1,y1]$,右端点在 $[x2,y2]$ 的所有子段的和的最大值。

$1\le n,m\le 10^4,x1\le y1,x2\le y2,x1\le x2,y1\le y2$。

恶心的分类讨论题……

首先发现,如果 $[x1,y1],[x2,y2]$ 不相交,那么所有的子段都包含了 $[y1+1,x2-1]$(如果 $y1<x2-1$),那么答案就是 $[x1,y1]$ 的最大右子段和加上 $[x2,y2]$ 的最大左子段和加上 $[y1+1,x2-1]$ 的和。

如果相交,那就很恶心了……

如果左右端点都在 $[x2,y1]$,那么答案就是 $[x2,y1]$ 的最大子段和。

如果左端点在 $[x2,y1]$,右端点在 $[y1+1,y2]$,答案就是 $[x2,y1]$ 的最大右子段和加上 $[x2+1,y2]$ 的最大左子段和。

如果左端点在 $[x1,x2-1]$,右端点在 $[x2,y2]$,答案就是 $[x1,x2-1]$ 的最大右段和加上 $[x2,y2]$ 的最大左子段和。

以上已经包含了所有的情况。这些取个最大值就好了。

时间复杂度 $O(m\log n)$。

代码中写得有一点点不一样,最重要的还是理解。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int maxn=;
  4. #define lson o<<1,l,mid
  5. #define rson o<<1|1,mid+1,r
  6. #define FOR(i,a,b) for(int i=(a);i<=(b);i++)
  7. #define ROF(i,a,b) for(int i=(a);i>=(b);i--)
  8. #define MEM(x,v) memset(x,v,sizeof(x))
  9. inline int read(){
  10. char ch=getchar();int x=,f=;
  11. while(ch<'' || ch>'') f|=ch=='-',ch=getchar();
  12. while(ch>='' && ch<='') x=x*+ch-'',ch=getchar();
  13. return f?-x:x;
  14. }
  15. struct node{
  16. int sum,lmax,rmax,amax;
  17. node operator+(const node &nd)const{
  18. node ans;
  19. ans.sum=sum+nd.sum;
  20. ans.lmax=max(lmax,sum+nd.lmax);
  21. ans.rmax=max(nd.rmax,nd.sum+rmax);
  22. ans.amax=max(amax,max(nd.amax,rmax+nd.lmax));
  23. return ans;
  24. }
  25. }nd[maxn*];
  26. int t,n,m,a[maxn];
  27. inline void pushup(int o){
  28. nd[o]=nd[o<<]+nd[o<<|];
  29. }
  30. void build(int o,int l,int r){
  31. if(l==r) return void(nd[o]=(node){a[l],a[l],a[l],a[l]});
  32. int mid=(l+r)>>;
  33. build(lson);build(rson);
  34. pushup(o);
  35. }
  36. node query(int o,int l,int r,int ql,int qr){
  37. if(ql>qr) return (node){,,,};
  38. if(l>=ql && r<=qr) return nd[o];
  39. int mid=(l+r)>>;
  40. if(mid<ql) return query(rson,ql,qr);
  41. if(mid>=qr) return query(lson,ql,qr);
  42. return query(lson,ql,qr)+query(rson,ql,qr);
  43. }
  44. int main(){
  45. t=read();
  46. while(t--){
  47. n=read();
  48. FOR(i,,n) a[i]=read();
  49. build(,,n);
  50. m=read();
  51. printf("m=%d\n",m);
  52. FOR(i,,m){
  53. int l1=read(),r1=read(),l2=read(),r2=read();
  54. if(r1<l2){
  55. node n1=query(,,n,l1,r1),n2=query(,,n,r1+,l2-),n3=query(,,n,l2,r2);
  56. printf("%d\n",n1.rmax+n2.sum+n3.lmax);
  57. }
  58. else{
  59. node n1=query(,,n,l2,r1);
  60. int ans=n1.amax;
  61. node n2=query(,,n,l1,l2-),n3=query(,,n,l2,r2);
  62. ans=max(ans,n2.rmax+n3.lmax);
  63. node n4=query(,,n,l1,r1),n5=query(,,n,r1+,r2);
  64. printf("%d\n",max(ans,n4.rmax+n5.lmax));
  65. }
  66. }
  67. }
  68. }

GSS5


GSS6:

给出一个由 $n$ 个整数组成的序列 $a$,你需要支持 $m$ 个操作:

  • I p x 在 $p$ 处插入插入一个元素 $x$
  • D p 删除 $p$ 处的一个元素
  • R p x 修改 $p$ 处元素的值为 $x$
  • Q l r 查询一个区间 $[l,r]$ 的最大子段和

$1\le n,m\le 10^5,|a_i|,|x|\le 10^4$。

这题一看就是splay(或者fhq treap)裸题。比维修数列好写多了(

如果您打过维修数列,这个就不用再讲了吧!

如果您会splay或者fhq treap,那么前三个操作应该不是问题,第四个操作也可以跟GSS1和GSS3一样维护 $lmax,rmax,amax$。

然后就没有了,码吧。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int maxn=;
  4. #define FOR(i,a,b) for(int i=(a);i<=(b);i++)
  5. #define ROF(i,a,b) for(int i=(a);i>=(b);i--)
  6. #define MEM(x,v) memset(x,v,sizeof(x))
  7. inline int read(){
  8. char ch=getchar();int x=,f=;
  9. while(ch<'' || ch>'') f|=ch=='-',ch=getchar();
  10. while(ch>='' && ch<='') x=x*+ch-'',ch=getchar();
  11. return f?-x:x;
  12. }
  13. int n,a[maxn],m,cnt,rt,fa[maxn],ch[maxn][],sz[maxn],val[maxn],sum[maxn],lmax[maxn],rmax[maxn],amax[maxn];
  14. void pushup(int x){
  15. sz[x]=sz[ch[x][]]+sz[ch[x][]]+;
  16. sum[x]=sum[ch[x][]]+sum[ch[x][]]+val[x];
  17. if(!ch[x][] && !ch[x][]) lmax[x]=rmax[x]=amax[x]=val[x];
  18. else if(!ch[x][]){
  19. lmax[x]=max(lmax[ch[x][]],sum[ch[x][]]+val[x]);
  20. rmax[x]=max(val[x],rmax[ch[x][]]+val[x]);
  21. amax[x]=max(val[x],max(rmax[ch[x][]]+val[x],amax[ch[x][]]));
  22. }
  23. else if(!ch[x][]){
  24. lmax[x]=max(val[x],val[x]+lmax[ch[x][]]);
  25. rmax[x]=max(rmax[ch[x][]],sum[ch[x][]]+val[x]);
  26. amax[x]=max(val[x],max(val[x]+lmax[ch[x][]],amax[ch[x][]]));
  27. }
  28. else{
  29. lmax[x]=max(lmax[ch[x][]],max(sum[ch[x][]]+val[x],sum[ch[x][]]+val[x]+lmax[ch[x][]]));
  30. rmax[x]=max(rmax[ch[x][]],max(sum[ch[x][]]+val[x],sum[ch[x][]]+val[x]+rmax[ch[x][]]));
  31. amax[x]=max(val[x],max(amax[ch[x][]],max(amax[ch[x][]],max(rmax[ch[x][]]+val[x],max(lmax[ch[x][]]+val[x],rmax[ch[x][]]+val[x]+lmax[ch[x][]])))));
  32. }
  33. }
  34. void rotate(int x){
  35. int y=fa[x],z=fa[y],t=ch[y][]==x,tt=ch[z][]==y;
  36. int B=ch[x][t^];
  37. fa[B]=y;ch[y][t]=B;
  38. fa[y]=x;ch[x][t^]=y;
  39. fa[x]=z;ch[z][tt]=x;
  40. pushup(y);pushup(x);
  41. }
  42. void splay(int x,int to){
  43. while(fa[x]!=to){
  44. int y=fa[x],z=fa[y],t=ch[y][]==x,tt=ch[z][]==y;
  45. if(z!=to) rotate(t^tt?x:y);
  46. rotate(x);
  47. }
  48. if(!to) rt=x;
  49. }
  50. int kth(int x){
  51. int now=rt;
  52. while(now){
  53. int s=sz[ch[now][]]+;
  54. if(x==s) return now;
  55. if(x<s) now=ch[now][];
  56. else x-=s,now=ch[now][];
  57. }
  58. }
  59. int build(int f,int l,int r){
  60. int mid=(l+r)>>,tmp=++cnt;
  61. fa[tmp]=f;
  62. val[tmp]=a[mid];
  63. if(l<mid) ch[tmp][]=build(tmp,l,mid-);
  64. if(mid<r) ch[tmp][]=build(tmp,mid+,r);
  65. pushup(tmp);
  66. return tmp;
  67. }
  68. void insert(int p,int x){
  69. int l=kth(p),r=kth(p+);
  70. splay(l,);splay(r,l);
  71. int &nd=ch[ch[rt][]][];
  72. nd=++cnt;
  73. fa[nd]=ch[rt][];
  74. val[nd]=x;
  75. pushup(nd);pushup(ch[rt][]);pushup(rt);
  76. }
  77. void remove(int p){
  78. int l=kth(p),r=kth(p+);
  79. splay(l,);splay(r,l);
  80. ch[ch[rt][]][]=;
  81. pushup(ch[rt][]);pushup(rt);
  82. }
  83. void replace(int p,int x){
  84. int l=kth(p),r=kth(p+);
  85. splay(l,);splay(r,l);
  86. val[ch[ch[rt][]][]]=x;
  87. pushup(ch[ch[rt][]][]);pushup(ch[rt][]);pushup(rt);
  88. }
  89. int query(int l,int r){
  90. l=kth(l);r=kth(r+);
  91. splay(l,);splay(r,l);
  92. return amax[ch[ch[rt][]][]];
  93. }
  94. int main(){
  95. n=read();
  96. FOR(i,,n) a[i]=read();
  97. rt=build(,,n+);
  98. m=read();
  99. FOR(i,,m){
  100. char op[];
  101. scanf("%s",op);
  102. int x=read();
  103. switch(op[]){
  104. case 'I':insert(x,read());break;
  105. case 'D':remove(x);break;
  106. case 'R':replace(x,read());break;
  107. case 'Q':printf("%d\n",query(x,read()));break;
  108. }
  109. }
  110. }

GSS6


GSS7:

给定一棵树,有 $n$ 个节点,每一个节点都有一个权值 $x_i$。

你需要执行 $q$ 次操作:

  1. 1 a b 查询 $(a,b)$ 这条链上的最大子段和,可以为空
  2. 2 a b c 将 $(a,b)$ 这条链的所有点权变为 $c$

$1\le n,q\le 10^5,|x_i|,|c|\le 10^4$。

裸的树剖,只不过查询时会比较麻烦。

我们可以先求出 $lca$ 到 $a$ 的最大子段和和 $lca$ 到 $b$ 的最大子段和。

具体求的话,就是可以将在上面的一条重链与下面已有的答案接起来。

最后把两个合并一下就好了。注意其中一个需要翻转后再拼。

具体看代码。(一个裸的树剖为什么比splay还长)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int maxn=,INF=;
  4. #define lson o<<1,l,mid
  5. #define rson o<<1|1,mid+1,r
  6. #define FOR(i,a,b) for(int i=(a);i<=(b);i++)
  7. #define ROF(i,a,b) for(int i=(a);i>=(b);i--)
  8. #define MEM(x,v) memset(x,v,sizeof(x))
  9. inline int read(){
  10. char ch=getchar();int x=,f=;
  11. while(ch<'' || ch>'') f|=ch=='-',ch=getchar();
  12. while(ch>='' && ch<='') x=x*+ch-'',ch=getchar();
  13. return f?-x:x;
  14. }
  15. int n,m,w[maxn],el,head[maxn],to[maxn*],nxt[maxn*];
  16. int fa[maxn],dep[maxn],sz[maxn],son[maxn],top[maxn],dfs_clock,id[maxn],dfn[maxn];
  17. int tag[maxn*];
  18. struct node{
  19. int sum,lmax,rmax,amax;
  20. node():sum(),lmax(),rmax(),amax(){}
  21. const node operator+(const node &r)const{
  22. node ans;
  23. ans.sum=sum+r.sum;
  24. ans.lmax=max(lmax,sum+r.lmax);
  25. ans.rmax=max(r.rmax,r.sum+rmax);
  26. ans.amax=max(amax,max(r.amax,rmax+r.lmax));
  27. return ans;
  28. }
  29. }nd[maxn*];
  30. inline void add(int u,int v){
  31. to[++el]=v;nxt[el]=head[u];head[u]=el;
  32. }
  33. void dfs1(int u,int f){
  34. dep[u]=dep[fa[u]=f]+;
  35. sz[u]=;
  36. for(int i=head[u];i;i=nxt[i]){
  37. int v=to[i];
  38. if(v==f) continue;
  39. dfs1(v,u);
  40. sz[u]+=sz[v];
  41. if(sz[v]>sz[son[u]]) son[u]=v;
  42. }
  43. }
  44. void dfs2(int u,int topf){
  45. top[u]=topf;
  46. id[dfn[u]=++dfs_clock]=u;
  47. if(son[u]) dfs2(son[u],topf);
  48. for(int i=head[u];i;i=nxt[i]){
  49. int v=to[i];
  50. if(v==fa[u] || v==son[u]) continue;
  51. dfs2(v,v);
  52. }
  53. }
  54. inline void cover(int o,int l,int r,int v){
  55. tag[o]=v;
  56. nd[o].sum=v*(r-l+);
  57. if(v>) nd[o].lmax=nd[o].rmax=nd[o].amax=nd[o].sum;
  58. else nd[o].lmax=nd[o].rmax=nd[o].amax=;
  59. }
  60. inline void pushdown(int o,int l,int r){
  61. if(tag[o]==-INF) return;
  62. int mid=(l+r)>>;
  63. cover(lson,tag[o]);cover(rson,tag[o]);
  64. tag[o]=-INF;
  65. }
  66. void build(int o,int l,int r){
  67. tag[o]=-INF;
  68. if(l==r){
  69. nd[o].sum=w[id[l]];
  70. nd[o].lmax=nd[o].rmax=nd[o].amax=max(,w[id[l]]);
  71. return;
  72. }
  73. int mid=(l+r)>>;
  74. build(lson);build(rson);
  75. nd[o]=nd[o<<]+nd[o<<|];
  76. }
  77. void update(int o,int l,int r,int ql,int qr,int v){
  78. if(l>=ql && r<=qr) return cover(o,l,r,v);
  79. int mid=(l+r)>>;
  80. pushdown(o,l,r);
  81. if(mid>=ql) update(lson,ql,qr,v);
  82. if(mid<qr) update(rson,ql,qr,v);
  83. nd[o]=nd[o<<]+nd[o<<|];
  84. }
  85. node query(int o,int l,int r,int ql,int qr){
  86. if(l>=ql && r<=qr) return nd[o];
  87. int mid=(l+r)>>;
  88. pushdown(o,l,r);
  89. if(mid<ql) return query(rson,ql,qr);
  90. if(mid>=qr) return query(lson,ql,qr);
  91. return query(lson,ql,qr)+query(rson,ql,qr);
  92. }
  93. void chain_update(int u,int v,int x){
  94. while(top[u]!=top[v]){
  95. if(dep[top[u]]<dep[top[v]]) swap(u,v);
  96. update(,,n,dfn[top[u]],dfn[u],x);
  97. u=fa[top[u]];
  98. }
  99. if(dep[u]>dep[v]) swap(u,v);
  100. update(,,n,dfn[u],dfn[v],x);
  101. }
  102. int chain_query(int u,int v){
  103. node un,vn;
  104. while(top[u]!=top[v]){
  105. if(dep[top[u]]<dep[top[v]]) swap(u,v),swap(un,vn);
  106. un=query(,,n,dfn[top[u]],dfn[u])+un;
  107. u=fa[top[u]];
  108. }
  109. if(dep[u]>dep[v]) swap(u,v),swap(un,vn);
  110. vn=query(,,n,dfn[u],dfn[v])+vn;
  111. swap(un.lmax,un.rmax);
  112. return (un+vn).amax;
  113. }
  114. int main(){
  115. n=read();
  116. FOR(i,,n) w[i]=read();
  117. FOR(i,,n-){
  118. int u=read(),v=read();
  119. add(u,v);add(v,u);
  120. }
  121. dfs1(,);dfs2(,);build(,,n);
  122. m=read();
  123. FOR(i,,m){
  124. int op=read(),a=read(),b=read();
  125. if(op==) chain_update(a,b,read());
  126. else printf("%d\n",chain_query(a,b));
  127. }
  128. }

GSS7


后面的,留坑待填……

SPOJ GSS系列的更多相关文章

  1. SPOJ GSS 系列

    来怒做GSS系列了: GSS1:https://www.luogu.org/problemnew/show/SP1043 这题就是维护一个 sum , mx , lmx , rmx,转移时用结构体就好 ...

  2. spoj GSS系列简要题解

    文章目录 GSS1 GSS2 GSS3 GSS4 GSS5 GSS6 GSS7 GSS8 传送门 这个GSSGSSGSS系列全部是跟子段有关的数据结构菜题. 于是来水一篇博客. GSS1 传送门 题意 ...

  3. SPOJ GSS系列(数据结构维护技巧入门)

    题目链接 GSS $GSS1$ 对于每个询问$l$, $r$,查询$a_{l}$, $a_{l+1}$, $a_{l+2}$, ..., $a_{r}$这个序列的最大字段和. 建立线段树,每个节点维护 ...

  4. 【SPOJ GSS】数据结构题选做

    SPOJ GSS1 题意:给一个序列以及一些询问,每个是问\([l,r]\)中最大连续子序列和是多少. 思路:这个问题是以下问题的基础. 我们考虑用线段树来解决这个问题. 首先我们来想想如果要求出最大 ...

  5. SPOJ GSS(Can you answer the Queries)系列 7/8

    GSS1 线段树最大子段和裸题,不带修改,注意pushup. 然而并不会猫树之类的东西 #include<bits/stdc++.h> #define MAXN 50001 using n ...

  6. SPOJ QTREE 系列解题报告

    题目一 : SPOJ 375 Query On a Tree http://www.spoj.com/problems/QTREE/ 给一个树,求a,b路径上最大边权,或者修改a,b边权为t. #in ...

  7. SPOJ GSS

    GSS1 题目大意:给出一个数列,多次询问区间最长连续子段和 题解:线段树维护区间最长连续子段和gss,区间从最左元素开始的最长连续子段和lgss 区间以最右元素为结尾的最长连续子段和rgss以及区间 ...

  8. GSS 系列题解

    GSS GSS1 随便猫树或者线段树,就可以过了 猫树不说,线段树可以维护左边最大,右边最大,区间最大,区间值然后就做出来了. //Isaunoya #pragma GCC optimize(2) # ...

  9. 激!GSS系列

    #include <cstdio> ; ; inline int max(int, int); inline int getint(); inline void putint(int); ...

随机推荐

  1. sklearn学习笔记之简单线性回归

    简单线性回归 线性回归是数据挖掘中的基础算法之一,从某种意义上来说,在学习函数的时候已经开始接触线性回归了,只不过那时候并没有涉及到误差项.线性回归的思想其实就是解一组方程,得到回归函数,不过在出现误 ...

  2. Linux df du 命令

    df 命令 检查磁盘空间占用情况(并不能查看某个目录占用的磁盘大小). 命令格式:df [option] -h 以容易理解的格式(给人看的格式)输出文件系统分区使用情况,例如 10kB.10MB.10 ...

  3. Centos7下ELK+Redis日志分析平台的集群环境部署记录

    之前的文档介绍了ELK架构的基础知识,日志集中分析系统的实施方案:- ELK+Redis- ELK+Filebeat - ELK+Filebeat+Redis- ELK+Filebeat+Kafka+ ...

  4. 20135218 实践四 ELF文件格式分析

    一 :概述 ELF全称Executable and Linkable Format,可执行连接格式,ELF格式的文件用于存储Linux程序.ELF文件(目标文件)格式主要三种: (1)可重定向文件:文 ...

  5. 软件工程——移动的HelloWorld

    package disiti;       import java.awt.Color;   import java.awt.Cursor;   import java.awt.Font;   imp ...

  6. SpringMvc配置扫包之后,访问路径404问题解决

    场景: 1. 配置了Spring和SpringMvc, Spring管理非Controller类的Bean, SpringMvc管理涉及的Controller类 2. web.xml已经配置了Spri ...

  7. 自己实现数据结构系列二---LinkedList

    一.先上代码: 1.方式一: public class LinkedList<E> { //节点,用来存放数据:数据+下一个元素的引用 private class Node{ privat ...

  8. STL数据结构

    priority_queue "C++ reference"上如此解释priority queue:"This context is similar to a heap, ...

  9. win10总是2分钟就自动睡眠怎么办 win10系统自动休眠bug怎么解决(转)

        解决方法如下: 1.右键点击开始图标,选择[运行],或者利用快捷键“win+R”打开运行窗口,win键是ctrl和alt键中间的徽标键:

  10. html5 视频和音频

    视频:html5支持视屏文件或者视屏流. html5使用video元素来播放视屏,支持的类型有OGG,MEPG 4,webM,但是不同的浏览器支持类型不同. src可以放置视屏文件的路径,可以使用元素 ...