SPOJ GSS 线段树系列

2023-10-27 14:59
文章标签 系列 线段 spoj gss

本文主要是介绍SPOJ GSS 线段树系列,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录


文章目录

  • 目录
  • [SP1043 GSS1 - Can you answer these queries I](https://www.luogu.com.cn/problem/SP1043)
  • [SP1716 GSS3 - Can you answer these queries III](https://www.luogu.com.cn/problem/SP1716)
  • [SP2713 GSS4 - Can you answer these queries IV](https://www.luogu.com.cn/problem/SP2713)
  • [SP2916 GSS5 - Can you answer these queries V](https://www.luogu.com.cn/problem/SP2916)
  • [SP6779 GSS7 - Can you answer these queries VII](https://www.luogu.com.cn/problem/SP6779)
  • [SP1557 GSS2 - Can you answer these queries II](https://www.luogu.com.cn/problem/SP1557)

SP1043 GSS1 - Can you answer these queries I

在这里插入图片描述
给出序列,查询区间的最大子段和。

  • 维护最大前缀子段和,最大后缀子段和,区间答案即可
  • 区间合并的时候,大区间的答案只能是左区间答案,右区间答案,左后缀+右前缀三者最大值
  • 查询过程可以返回node,重载 + 来合并答案
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ll long long
#define pii pair<int,int>
#define re register
#define lc rt<<1
#define rc rt<<1|1
const int maxn = 5e5+10;
const int mx = 40;
const ll mod = 998244353;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
//给定序列 q次询问 每次询问一个区间内的最大子段和
//记录答案 区间第一个数开始的最大子段和  区间最后一个数结尾的最大子段和  区间合并
int n,m;
int a[maxn];
int sum[maxn];//前缀和
struct node 
{int l,r;int ans,pre,suf;node operator + (const node &f)const {node t;t.ans=max(max(ans,f.ans),suf+f.pre);t.pre=max(pre,sum[r]-sum[l-1]+f.pre);//不加入/加入右区间的影响  t.suf=max(f.suf,suf+sum[f.r]-sum[f.l-1]);t.l=l,t.r=f.r;return t;}
}tree[maxn<<2];
inline void pushup(int rt) 
{tree[rt]=tree[lc]+tree[rc];
}
inline void build(int rt,int l,int r) 
{tree[rt].l=l,tree[rt].r=r;if(l == r) {int d=a[l];tree[rt].pre=tree[rt].suf=tree[rt].ans=d;return ;}int mid=l+r>>1;build(lc,l,mid);build(rc,mid+1,r);pushup(rt);
}
inline node query(int rt,int vl,int vr) 
{int l=tree[rt].l,r=tree[rt].r;if(vl<=l && r<=vr) return tree[rt];int mid=l+r>>1;if(vr<=mid) return query(lc,vl,vr);else if(vl > mid) return query(rc,vl,vr);return query(lc,vl,vr)+query(rc,vl,vr);
}
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",a+i);for(int i=1;i<=n;i++) sum[i] = a[i]+sum[i-1];build(1,1,n);scanf("%d",&m);while(m--) {int l,r;scanf("%d %d",&l,&r);printf("%d\n",query(1,l,r).ans);}return 0;
}

SP1716 GSS3 - Can you answer these queries III

和1类似,只不过多了个单点修改的操作。

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ll long long
#define pii pair<int,int>
#define re register
#define lc rt<<1
#define rc rt<<1|1
const int maxn = 5e4+10;
const int mx = 40;
const ll mod = 998244353;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
struct node 
{ll ans,pre,suf,sum;int l,r;node operator + (const node &f)const {node t;t.ans=max(suf+f.pre,max(ans,f.ans));t.pre=max(pre,sum+f.pre);t.suf=max(f.suf,f.sum+suf);t.sum=sum+f.sum;t.l=l,t.r=f.r;return t;}
}tree[maxn<<2];
inline void pushup(int rt) 
{tree[rt]=tree[lc]+tree[rc];
}
void build(int rt,int l,int r) 
{tree[rt].l=l,tree[rt].r=r;if(l == r) {scanf("%lld",&tree[rt].ans);tree[rt].pre=tree[rt].suf=tree[rt].sum=tree[rt].ans;return ;}int mid=l+r>>1;build(lc,l,mid);build(rc,mid+1,r);pushup(rt);
}
inline void upd(int rt,int pos,int v) 
{int l=tree[rt].l,r=tree[rt].r;if(l==r) {tree[rt].sum=tree[rt].ans=v;tree[rt].pre=tree[rt].suf=v;return ;}int mid=l+r>>1;if(pos<=mid) upd(lc,pos,v);else upd(rc,pos,v);pushup(rt);
}
inline node query(int rt,int vl,int vr) 
{int l=tree[rt].l,r=tree[rt].r;if(vl<=l && r<=vr) return tree[rt];int mid=l+r>>1;if(vr<=mid) return query(lc,vl,vr);else if(vl>mid) return query(rc,vl,vr);return query(lc,vl,vr)+query(rc,vl,vr);
}
int n,m;
int main()
{scanf("%d",&n);build(1,1,n);scanf("%d",&m);while(m--) {int f,x,y;scanf("%d %d %d",&f,&x,&y);if(!f) {upd(1,x,y);}else printf("%lld\n",query(1,x,y).ans);}return 0;
}

SP2713 GSS4 - Can you answer these queries IV

在这里插入图片描述

  • 区间每个数开根号,经典的单点暴力修改,每个点只能被修改log次。
  • 记录最大值,如果最大值小于等于1,剪枝。
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ll long long
#define pii pair<int,int>
#define re register
#define lc rt<<1
#define rc rt<<1|1
const int maxn = 1e5+10;
const int mx = 40;
const ll mod = 998244353;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
//区间每个数开方 区间求和 记录最大值 暴力单点修改
struct node 
{int l,r;ll mmax,sum;node operator + (const node &f)const{node t;t.mmax=max(mmax,f.mmax);t.sum=sum+f.sum;t.l=l,t.r=f.r;return t;} 
}tree[maxn<<2];
inline void pushup(int rt)
{tree[rt]=tree[lc]+tree[rc];
}
inline void build(int rt,int l ,int r)
{tree[rt].l=l,tree[rt].r=r;if(l==r) {scanf("%lld",&tree[rt].sum);tree[rt].mmax=tree[rt].sum;return ;}int mid=l+r>>1;build(lc,l,mid);build(rc,mid+1,r);pushup(rt);
}
inline void upd(int rt,int vl,int vr) 
{int l=tree[rt].l,r=tree[rt].r;if(tree[rt].mmax <= 1 ||  l > vr || r<vl) return;//剪枝if(l == r) {ll d=(ll)sqrt(tree[rt].mmax);//    cout<<"--"<<tree[rt].mmax<<' '<<d<<'\n';tree[rt].mmax=tree[rt].sum=d;return ;}int mid=l+r>>1;upd(lc,vl,vr);upd(rc,vl,vr);pushup(rt);
}
inline node query(int rt,int vl,int vr)
{int l=tree[rt].l,r=tree[rt].r;if(vl<=l && r<=vr) return tree[rt];int mid=l+r>>1;if(vr<=mid) return query(lc,vl,vr);else if(vl>mid) return query(rc,vl,vr);return query(lc,vl,vr)+query(rc,vl,vr);
}
int n,cnt,m;
int main()
{while(~scanf("%d",&n)) {build(1,1,n);scanf("%d",&m);printf("Case #%d:\n",++cnt);while(m--){int f,x,y;scanf("%d %d %d",&f,&x,&y);if(x>y) swap(x,y);if(!f) {upd(1,x,y);}else printf("%lld\n",query(1,x,y).sum);}puts("");}return 0;
}

SP2916 GSS5 - Can you answer these queries V

在这里插入图片描述

  • 这题咋一看很麻烦,但如果能从分类讨论的方向着手,会发现其实就是在第一题的基础上加了点东西。我们维护的信息和第一题一样。

  • 首先给出的[x1,y1],[x2,y2]只有两种情况,①y1<=x2,也就是两个区间中间隔了至少一个数 ②y1>x2 ,两个区间有相交

  • 对于①,只有一种情况在这里插入图片描述
    我们发现无论左右端点怎么选,[y1,x2]这段是必选的,所以答案就是[y1,x2]的区间和加上max(0,[x1,y1-1]的后缀答案),再加上max(0,[x2+1,y2]的前缀答案)

对于②: 我们有4种情况
在这里插入图片描述

  • (1) 的情况就是左右端点都在中间,所选的区间即为[x2,y1]的子区间,所以答案就是[x2,y1]的ans
  • (2) 的情况就是左端点在[x1,x2],右端点在[x2,y1] ,x2这个点必取,然后加[x1,x2-1]的后缀,[x2+1,y1]的前缀
  • (3)的情况和(2)对称的
  • (4)左端点在[x1,x2] 右端点在[y1,y2], 中间段的区间和必取,然后加上[x1,x2-1]的后缀,[y1+1,y2]的前缀
  • 4种情况取max即可,注意区间查询前要判断l<=r
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ll long long
#define pii pair<int,int>
#define lc rt<<1
#define rc rt<<1|1
const int maxn = 1e4+10;
const int mx = 40;
const ll mod = 998244353;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
struct node 
{int l,r;int sum,ans,pre,suf;node operator + (const node &f)const {node t;t.l=l,t.r=f.r;t.ans=max(suf+f.pre,max(ans,f.ans));t.sum=sum+f.sum;t.pre=max(pre,sum+f.pre);t.suf=max(f.suf,f.sum+suf);return t;}
}tree[maxn<<2];
inline void pushup(int rt) 
{tree[rt]=tree[lc]+tree[rc];
}
inline void build(int rt,int l,int r) 
{tree[rt].l=l,tree[rt].r=r;if(l == r) {scanf("%d",&tree[rt].ans);tree[rt].sum=tree[rt].pre=tree[rt].suf=tree[rt].ans;return ;}int mid=l+r>>1;build(lc,l,mid);build(rc,mid+1,r);pushup(rt);
}
inline node query(int rt,int vl,int vr) 
{int l=tree[rt].l,r=tree[rt].r;if(vl<=l && r<=vr) return tree[rt];int mid=l+r>>1;if(vr<=mid) return query(lc,vl,vr);else if(vl>mid) return query(rc,vl,vr);return query(lc,vl,vr)+query(rc,vl,vr);
}
int n;
int main()
{int t;cin>>t;while(t--) {scanf("%d",&n);build(1,1,n);int m;scanf("%d",&m);while(m--) {int x1,y1,x2,y2;scanf("%d %d %d %d",&x1,&y1,&x2,&y2);if(y1<=x2) {int ans=query(1,y1,x2).sum;//中间段必选if(x1<=y1-1)ans=max(ans,ans+query(1,x1,y1-1).suf);if(x2+1<=y2)ans=max(ans,ans+query(1,x2+1,y2).pre);printf("%d\n",ans);}else {int ans1=query(1,x2,y1).ans;//只选[x2,y1]int ans2=query(1,x2,x2).sum;if(x1<=x2-1)ans2=max(ans2,ans2+query(1,x1,x2-1).suf);if(x2+1<=y1)ans2=max(ans2,ans2+query(1,x2+1,y1).pre);int ans3=query(1,y1,y1).sum;if(x2<=y1-1)ans3=max(ans3,ans3+query(1,x2,y1-1).suf);if(y1+1<=y2)ans3=max(ans3,ans3+query(1,y1+1,y2).pre);int ans4=query(1,x2,y1).sum;if(x1<=x2-1)ans4=max(ans4,ans4+query(1,x1,x2-1).suf);if(y1+1<=y2)ans4=max(ans4,ans4+query(1,y1+1,y2).pre);printf("%d\n",max(max(ans1,ans2),max(ans3,ans4)));}}}return 0;
}

SP6779 GSS7 - Can you answer these queries VII

在这里插入图片描述
刚开始以为只是把第一题加了区间修改并且搬到树上,导致在树剖查询跳链的时候没有注意区间合并的方向性,一直wa…

  • 线段树区间合并的时候,是左区间的pre和右区间的suf合并出新的ans
  • 对于查询[X,Y],我们不妨把LCA->X这条链记为左链,LCA->Y记为右链,我们发现,左链对应在dfs序中是几段不相连的子段,右链同样如此,所以我们需要左右链各自跳链,独立记录答案,最后在LCA处合并答案 。
  • 此外有一个要注意的是,左链在合并的时候是把深度小的顶点作为左边,深度大的作为右边 , 然而我们查询X->Y是把深度大的作为左边,所以需要交换一下左链的pre和suf ,再进行最终的合并。
  • 链的修改就是寻常的跳链。
    在这里插入图片描述
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ll long long
#define pii pair<int,int>
#define re register
const int maxn = 1e5+10;
const int mx = 40;
const ll mod = 998244353;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
//给定一棵树 查节点a到b简单路径上 点权的最大子段和(可以为空)  、 a到b路径点权改为c 
//树剖+线段树
//维护和查询过程不考虑空段情况 最后和0取max即可
//区间合并具有方向性 即左区间的后缀和右区间的前缀合并
struct 
{int to,next;
}e[maxn<<1];
int head[maxn],cnt=0;
inline void add(int v,int u)
{e[++cnt].to=u;e[cnt].next=head[v];head[v]=cnt;
}
int dep[maxn],fa[maxn],son[maxn],siz[maxn];
void dfs1(int rt,int par)
{siz[rt]=1;for(int i=head[rt];i;i=e[i].next) {int to=e[i].to;if(to==par) continue;fa[to]=rt;dep[to]=dep[rt]+1;dfs1(to,rt);siz[rt]+=siz[to];if(siz[to] > siz[son[rt]]) son[rt]=to;}
}
int top[maxn],dfsc,in[maxn],pos[maxn];
void dfs2(int rt,int t) 
{top[rt]=t;in[rt]=++dfsc;pos[dfsc]=rt;if(son[rt]) dfs2(son[rt],t);for(int i=head[rt];i;i=e[i].next){int to=e[i].to;if(to==fa[rt] || to==son[rt]) continue;dfs2(to,to);}
}
int a[maxn],n;
struct node 
{#define lc rt<<1#define rc rt<<1|1int l,r;int ans,pre,suf,sum;int tag;node operator + (const node &f)const {node t;t.l=l,t.r=f.r;t.sum=sum+f.sum;t.ans=max(max(ans,f.ans),suf+f.pre);t.pre=max(pre,sum+f.pre);t.suf=max(f.suf,f.sum+suf);t.tag=maxn;//子区间已经递归下去了 此时rt的tag一定是"0"状态return t;}
}tree[maxn<<2];
inline void pushup(int rt)
{tree[rt]=tree[lc]+tree[rc];
}
inline void build(int rt,int l,int r) 
{tree[rt].l=l,tree[rt].r=r;tree[rt].tag=maxn;if(l == r) {tree[rt].pre=tree[rt].suf=tree[rt].ans=a[pos[l]];tree[rt].sum=a[pos[l]];return ;}int mid=l+r>>1;build(lc,l,mid);build(rc,mid+1,r);pushup(rt);
}
inline void change(int rt,int len,int c) 
{tree[rt].pre=tree[rt].suf=tree[rt].ans=max(c,c*len);//负数则取c  非负数取c*lentree[rt].sum=c*len;tree[rt].tag=c;
}
inline void pushdown(int rt) 
{if(tree[rt].tag != maxn) {int l=tree[rt].l,r=tree[rt].r;int mid=(l+r)>>1;change(lc,mid-l+1,tree[rt].tag);change(rc,r-mid,tree[rt].tag);tree[rt].tag=maxn;}
}
inline void upd(int rt,int vl,int vr,int c)//[vl,vr]改为c 
{int l=tree[rt].l,r=tree[rt].r;if(l>vr || r<vl) return ;if(vl<=l && r<=vr) {change(rt,r-l+1,c);return ;}int mid=l+r>>1;pushdown(rt);upd(lc,vl,vr,c);upd(rc,vl,vr,c);pushup(rt);
}
inline node query(int rt,int vl,int vr) 
{int l=tree[rt].l,r=tree[rt].r;if(vl<=l && r<=vr) return tree[rt];int mid=l+r>>1;pushdown(rt);if(vr<=mid) return query(lc,vl,vr);else if(vl>mid) return query(rc,vl,vr);return query(lc,vl,vr)+query(rc,vl,vr);
}
//x到y 对应dfs序的几段不衔接的区间
//假设x是LCA左边的  y是LCA右边的
//我们需要左右各自跳链+合并
inline node chain_q(int x,int y) 
{node ans1=(node){0,0,0,0,0,0},ans2=(node){0,0,0,0,0,0};//左边链的答案 右边链的答案int fx=top[x],fy=top[y];while(fx != fy) {if(dep[fx] < dep[fy]) { //链顶深度大的往上跳ans2=query(1,in[fy],in[y])+ans2;//加号两边的顺序不能写反y=fa[fy];fy=top[y];}else {//链顶深度大的往上跳 //ans1的方向是LCA->X  ,但是答案需要的是X->LCA,所以最后需要交换ans1的pre和suf ans1=query(1,in[fx],in[x])+ans1;//加号两边的顺序不能写反x=fa[fx];fx=top[x];}}//x在下面if(in[y] < in[x]) ans1=query(1,in[y],in[x])+ans1;//加号两边的顺序不能写反//y在下面else ans2=query(1,in[x],in[y])+ans2;//加号两边的顺序不能写反//x->LCA这条链我们算的pre suf其实是LCA->x的方向,所以需要交换一下,然后和ans2进行合并swap(ans1.pre,ans1.suf);return ans1+ans2;//加号两边的顺序不能写反
}
//链的修改是寻常跳链
inline void chain_u(int x,int y,int c) 
{int fx=top[x],fy=top[y];while(fx != fy) {if(dep[fx] < dep[fy]) swap(x,y),swap(fx,fy);upd(1,in[fx],in[x],c);x=fa[fx];fx=top[x];}//x y此时在同一条链 深度小的就是LCAif(in[x] > in[y]) swap(x,y);upd(1,in[x],in[y],c);
}
void init()
{cnt=0,dfsc=0,dep[1]=0;for(int i=1;i<=n;i++) {scanf("%d",a+i);head[i]=0;son[i]=0;}for(int i=1;i<n;i++) {int u,v;scanf("%d %d",&u,&v);add(u,v),add(v,u);}
}
int main()
{   scanf("%d",&n);init();//树剖dfs1(1,1);dfs2(1,1);//建树build(1,1,n);int m;scanf("%d",&m);while(m--) {int f,x,y,c;scanf("%d",&f);if(f==1) {scanf("%d %d",&x,&y);//可以为空段 所以和0取maxprintf("%d\n",max(0,chain_q(x,y).ans));}else {scanf("%d %d %d",&x,&y,&c);chain_u(x,y,c);}}return 0;
}

SP1557 GSS2 - Can you answer these queries II

在这里插入图片描述

应该是最难的一题了咕咕咕

这篇关于SPOJ GSS 线段树系列的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/286439

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

poj 1127 线段相交的判定

题意: 有n根木棍,每根的端点坐标分别是 px, py, qx, qy。 判断每对木棍是否相连,当他们之间有公共点时,就认为他们相连。 并且通过相连的木棍相连的木棍也是相连的。 解析: 线段相交的判定。 首先,模板中的线段相交是不判端点的,所以要加一个端点在直线上的判定; 然后,端点在直线上的判定这个函数是不判定两个端点是同一个端点的情况的,所以要加是否端点相等的判断。 最后

HDU4737线段树

题目大意:给定一系列数,F(i,j)表示对从ai到aj连续求或运算,(i<=j)求F(i,j)<=m的总数。 const int Max_N = 100008 ;int sum[Max_N<<2] , x[Max_N] ;int n , m ;void push_up(int t){sum[t] = sum[t<<1] | sum[t<<1|1] ;}void upd