众所周知的仅次于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)$。
具体可以看代码。(那是好久以前写的了,所以会有点丑)
#include<bits/stdc++.h>
using namespace std;
const int maxm=;
namespace Segment_Tree{
struct node{
int sum,lmax,rmax,amax;
}e[maxm];
int n;
inline void pushup(int t){
node ls=e[t<<],rs=e[t<<|];
e[t]=(node){
ls.sum+rs.sum,
max(ls.lmax,ls.sum+rs.lmax),
max(rs.rmax,rs.sum+ls.rmax),
max(max(ls.amax,rs.amax),ls.rmax+rs.lmax)
};
}
void build_(int l,int r,int t){
if(l==r){
scanf("%d",&e[t].sum);
e[t]=(node){e[t].sum,e[t].sum,e[t].sum,e[t].sum};
return;
}
int mid=l+r>>;
build_(l,mid,t<<);
build_(mid+,r,t<<|);
pushup(t);
}
void build(int x){
n=x;
build_(,x,);
}
node query_(int L,int R,int l,int r,int t){
if(L>=l && R<=r) return e[t];
int mid=L+R>>;
if(mid>=r) return query_(L,mid,l,r,t<<);
if(mid<l) return query_(mid+,R,l,r,t<<|);
node ans,ls,rs;
ls=query_(L,mid,l,r,t<<);rs=query_(mid+,R,l,r,t<<|);
ans.sum=ls.sum+rs.sum;
ans.lmax=max(ls.lmax,ls.sum+rs.lmax);
ans.rmax=max(rs.rmax,rs.sum+ls.rmax);
ans.amax=max(max(ls.amax,rs.amax),ls.rmax+rs.lmax);
return ans;
}
int query(int l,int r){
return query_(,n,l,r,).amax;
}
}
int n,m;
int main(){
scanf("%d",&n);
Segment_Tree::build(n);
scanf("%d",&m);
for(int i=;i<=m;i++){
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",Segment_Tree::query(l,r));
}
}
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))$。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=;
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
char ch=getchar();int x=,f=;
while(ch<'' || ch>'') f|=ch=='-',ch=getchar();
while(ch>='' && ch<='') x=x*+ch-'',ch=getchar();
return f?-x:x;
}
struct query{
int l,r,id;
bool operator<(const query &q)const{
return r<q.r;
}
}qqq[maxn];
int n,q,pre[maxn*],a[maxn];
ll ans[maxn],sum[maxn*],hmax[maxn*],tag[maxn*],htag[maxn*];
void pushup(int o){
sum[o]=max(sum[o<<],sum[o<<|]);
hmax[o]=max(hmax[o<<],hmax[o<<|]);
}
void pushdown(int o){
hmax[o<<]=max(hmax[o<<],sum[o<<]+htag[o]);
hmax[o<<|]=max(hmax[o<<|],sum[o<<|]+htag[o]);
sum[o<<]+=tag[o];
sum[o<<|]+=tag[o];
htag[o<<]=max(htag[o<<],tag[o<<]+htag[o]);
htag[o<<|]=max(htag[o<<|],tag[o<<|]+htag[o]);
tag[o<<]+=tag[o];
tag[o<<|]+=tag[o];
tag[o]=htag[o]=;
}
void update(int o,int l,int r,int ql,int qr,int x){
if(l>=ql && r<=qr){
sum[o]+=x;
hmax[o]=max(hmax[o],sum[o]);
tag[o]+=x;
htag[o]=max(htag[o],tag[o]);
return;
}
pushdown(o);
int mid=(l+r)>>;
if(mid>=ql) update(lson,ql,qr,x);
if(mid<qr) update(rson,ql,qr,x);
pushup(o);
}
ll query(int o,int l,int r,int ql,int qr){
if(l>=ql && r<=qr) return hmax[o];
pushdown(o);
int mid=(l+r)>>;ll ans=-1e18;
if(mid>=ql) ans=max(ans,query(lson,ql,qr));
if(mid<qr) ans=max(ans,query(rson,ql,qr));
return ans;
}
int main(){
n=read();
FOR(i,,n) a[i]=read();
q=read();
FOR(i,,q) qqq[i].l=read(),qqq[i].r=read(),qqq[i].id=i;
sort(qqq+,qqq+q+);
int curr=;
FOR(i,,q){
while(curr<=qqq[i].r){
update(,,n,pre[a[curr]+]+,curr,a[curr]);
pre[a[curr]+]=curr;
curr++;
}
ans[qqq[i].id]=query(,,n,qqq[i].l,qqq[i].r);
}
FOR(i,,q) printf("%lld\n",ans[i]);
}
GSS2
GSS3:
长度为 $n$ 的序列 $a$,$m$ 个操作:单点修改,或询问区间的最大子段和。
$1\le n,m\le 5\times 10^4$。
跟GSS1一样,不说了。
#include<bits/stdc++.h>
using namespace std;
const int maxn=;
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
char ch=getchar();int x=,f=;
while(ch<'' || ch>'') f|=ch=='-',ch=getchar();
while(ch>='' && ch<='') x=x*+ch-'',ch=getchar();
return f?-x:x;
}
struct node{
int sum,lmax,rmax,amax;
}nd[maxn*];
int n,q,a[maxn];
void pushup(node &o,node l,node r){
o.sum=l.sum+r.sum;
o.lmax=max(l.lmax,l.sum+r.lmax);
o.rmax=max(r.rmax,r.sum+l.rmax);
o.amax=max(l.amax,max(r.amax,l.rmax+r.lmax));
}
void build(int o,int l,int r){
if(l==r) return void(nd[o]=(node){a[l],a[l],a[l],a[l]});
int mid=(l+r)>>;
build(lson);
build(rson);
pushup(nd[o],nd[o<<],nd[o<<|]);
}
void update(int o,int l,int r,int p,int x){
if(l==r) return void(nd[o]=(node){x,x,x,x});
int mid=(l+r)>>;
if(mid>=p) update(lson,p,x);
else update(rson,p,x);
pushup(nd[o],nd[o<<],nd[o<<|]);
}
node query(int o,int l,int r,int ql,int qr){
if(l>=ql && r<=qr) return nd[o];
int mid=(l+r)>>;
if(mid<ql) return query(rson,ql,qr);
if(mid>=qr) return query(lson,ql,qr);
node ans;
pushup(ans,query(lson,ql,qr),query(rson,ql,qr));
return ans;
}
int main(){
n=read();
FOR(i,,n) a[i]=read();
build(,,n);
q=read();
FOR(i,,q){
int op=read(),l=read(),r=read();
if(op) printf("%d\n",query(,,n,l,r).amax);
else update(,,n,l,r);
}
}
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)$。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=;
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
char ch=getchar();ll x=,f=;
while(ch<'' || ch>'') f|=ch=='-',ch=getchar();
while(ch>='' && ch<='') x=x*+ch-'',ch=getchar();
return f?-x:x;
}
int t,n,q;
ll a[maxn],sum[maxn*];
bool all1[maxn*];
inline void pushup(int o){
sum[o]=sum[o<<]+sum[o<<|];
all1[o]=all1[o<<]&all1[o<<|];
}
void build(int o,int l,int r){
if(l==r) return void((sum[o]=a[l],all1[o]=(a[l]==)));
int mid=(l+r)>>;
build(lson);
build(rson);
pushup(o);
}
void update(int o,int l,int r,int ql,int qr){
if(all1[o]) return;
if(l==r) return void((sum[o]=sqrt(sum[o]),all1[o]=(sum[o]==)));
int mid=(l+r)>>;
if(mid>=ql) update(lson,ql,qr);
if(mid<qr) update(rson,ql,qr);
pushup(o);
}
ll query(int o,int l,int r,int ql,int qr){
if(l>=ql && r<=qr) return sum[o];
int mid=(l+r)>>;ll s=;
if(mid>=ql) s+=query(lson,ql,qr);
if(mid<qr) s+=query(rson,ql,qr);
return s;
}
int main(){
while(~scanf("%d",&n)){
printf("Case #%d:\n",++t);
FOR(i,,n) a[i]=read();
build(,,n);
q=read();
FOR(i,,q){
int op=read(),l=read(),r=read();
if(l>r) swap(l,r);
if(op) printf("%lld\n",query(,,n,l,r));
else update(,,n,l,r);
}
puts("");
}
}
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)$。
代码中写得有一点点不一样,最重要的还是理解。
#include<bits/stdc++.h>
using namespace std;
const int maxn=;
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
char ch=getchar();int x=,f=;
while(ch<'' || ch>'') f|=ch=='-',ch=getchar();
while(ch>='' && ch<='') x=x*+ch-'',ch=getchar();
return f?-x:x;
}
struct node{
int sum,lmax,rmax,amax;
node operator+(const node &nd)const{
node ans;
ans.sum=sum+nd.sum;
ans.lmax=max(lmax,sum+nd.lmax);
ans.rmax=max(nd.rmax,nd.sum+rmax);
ans.amax=max(amax,max(nd.amax,rmax+nd.lmax));
return ans;
}
}nd[maxn*];
int t,n,m,a[maxn];
inline void pushup(int o){
nd[o]=nd[o<<]+nd[o<<|];
}
void build(int o,int l,int r){
if(l==r) return void(nd[o]=(node){a[l],a[l],a[l],a[l]});
int mid=(l+r)>>;
build(lson);build(rson);
pushup(o);
}
node query(int o,int l,int r,int ql,int qr){
if(ql>qr) return (node){,,,};
if(l>=ql && r<=qr) return nd[o];
int mid=(l+r)>>;
if(mid<ql) return query(rson,ql,qr);
if(mid>=qr) return query(lson,ql,qr);
return query(lson,ql,qr)+query(rson,ql,qr);
}
int main(){
t=read();
while(t--){
n=read();
FOR(i,,n) a[i]=read();
build(,,n);
m=read();
printf("m=%d\n",m);
FOR(i,,m){
int l1=read(),r1=read(),l2=read(),r2=read();
if(r1<l2){
node n1=query(,,n,l1,r1),n2=query(,,n,r1+,l2-),n3=query(,,n,l2,r2);
printf("%d\n",n1.rmax+n2.sum+n3.lmax);
}
else{
node n1=query(,,n,l2,r1);
int ans=n1.amax;
node n2=query(,,n,l1,l2-),n3=query(,,n,l2,r2);
ans=max(ans,n2.rmax+n3.lmax);
node n4=query(,,n,l1,r1),n5=query(,,n,r1+,r2);
printf("%d\n",max(ans,n4.rmax+n5.lmax));
}
}
}
}
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$。
然后就没有了,码吧。
#include<bits/stdc++.h>
using namespace std;
const int maxn=;
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
char ch=getchar();int x=,f=;
while(ch<'' || ch>'') f|=ch=='-',ch=getchar();
while(ch>='' && ch<='') x=x*+ch-'',ch=getchar();
return f?-x:x;
}
int n,a[maxn],m,cnt,rt,fa[maxn],ch[maxn][],sz[maxn],val[maxn],sum[maxn],lmax[maxn],rmax[maxn],amax[maxn];
void pushup(int x){
sz[x]=sz[ch[x][]]+sz[ch[x][]]+;
sum[x]=sum[ch[x][]]+sum[ch[x][]]+val[x];
if(!ch[x][] && !ch[x][]) lmax[x]=rmax[x]=amax[x]=val[x];
else if(!ch[x][]){
lmax[x]=max(lmax[ch[x][]],sum[ch[x][]]+val[x]);
rmax[x]=max(val[x],rmax[ch[x][]]+val[x]);
amax[x]=max(val[x],max(rmax[ch[x][]]+val[x],amax[ch[x][]]));
}
else if(!ch[x][]){
lmax[x]=max(val[x],val[x]+lmax[ch[x][]]);
rmax[x]=max(rmax[ch[x][]],sum[ch[x][]]+val[x]);
amax[x]=max(val[x],max(val[x]+lmax[ch[x][]],amax[ch[x][]]));
}
else{
lmax[x]=max(lmax[ch[x][]],max(sum[ch[x][]]+val[x],sum[ch[x][]]+val[x]+lmax[ch[x][]]));
rmax[x]=max(rmax[ch[x][]],max(sum[ch[x][]]+val[x],sum[ch[x][]]+val[x]+rmax[ch[x][]]));
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][]])))));
}
}
void rotate(int x){
int y=fa[x],z=fa[y],t=ch[y][]==x,tt=ch[z][]==y;
int B=ch[x][t^];
fa[B]=y;ch[y][t]=B;
fa[y]=x;ch[x][t^]=y;
fa[x]=z;ch[z][tt]=x;
pushup(y);pushup(x);
}
void splay(int x,int to){
while(fa[x]!=to){
int y=fa[x],z=fa[y],t=ch[y][]==x,tt=ch[z][]==y;
if(z!=to) rotate(t^tt?x:y);
rotate(x);
}
if(!to) rt=x;
}
int kth(int x){
int now=rt;
while(now){
int s=sz[ch[now][]]+;
if(x==s) return now;
if(x<s) now=ch[now][];
else x-=s,now=ch[now][];
}
}
int build(int f,int l,int r){
int mid=(l+r)>>,tmp=++cnt;
fa[tmp]=f;
val[tmp]=a[mid];
if(l<mid) ch[tmp][]=build(tmp,l,mid-);
if(mid<r) ch[tmp][]=build(tmp,mid+,r);
pushup(tmp);
return tmp;
}
void insert(int p,int x){
int l=kth(p),r=kth(p+);
splay(l,);splay(r,l);
int &nd=ch[ch[rt][]][];
nd=++cnt;
fa[nd]=ch[rt][];
val[nd]=x;
pushup(nd);pushup(ch[rt][]);pushup(rt);
}
void remove(int p){
int l=kth(p),r=kth(p+);
splay(l,);splay(r,l);
ch[ch[rt][]][]=;
pushup(ch[rt][]);pushup(rt);
}
void replace(int p,int x){
int l=kth(p),r=kth(p+);
splay(l,);splay(r,l);
val[ch[ch[rt][]][]]=x;
pushup(ch[ch[rt][]][]);pushup(ch[rt][]);pushup(rt);
}
int query(int l,int r){
l=kth(l);r=kth(r+);
splay(l,);splay(r,l);
return amax[ch[ch[rt][]][]];
}
int main(){
n=read();
FOR(i,,n) a[i]=read();
rt=build(,,n+);
m=read();
FOR(i,,m){
char op[];
scanf("%s",op);
int x=read();
switch(op[]){
case 'I':insert(x,read());break;
case 'D':remove(x);break;
case 'R':replace(x,read());break;
case 'Q':printf("%d\n",query(x,read()));break;
}
}
}
GSS6
GSS7:
给定一棵树,有 $n$ 个节点,每一个节点都有一个权值 $x_i$。
你需要执行 $q$ 次操作:
-
1 a b
查询 $(a,b)$ 这条链上的最大子段和,可以为空 -
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还长)
#include<bits/stdc++.h>
using namespace std;
const int maxn=,INF=;
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
char ch=getchar();int x=,f=;
while(ch<'' || ch>'') f|=ch=='-',ch=getchar();
while(ch>='' && ch<='') x=x*+ch-'',ch=getchar();
return f?-x:x;
}
int n,m,w[maxn],el,head[maxn],to[maxn*],nxt[maxn*];
int fa[maxn],dep[maxn],sz[maxn],son[maxn],top[maxn],dfs_clock,id[maxn],dfn[maxn];
int tag[maxn*];
struct node{
int sum,lmax,rmax,amax;
node():sum(),lmax(),rmax(),amax(){}
const node operator+(const node &r)const{
node ans;
ans.sum=sum+r.sum;
ans.lmax=max(lmax,sum+r.lmax);
ans.rmax=max(r.rmax,r.sum+rmax);
ans.amax=max(amax,max(r.amax,rmax+r.lmax));
return ans;
}
}nd[maxn*];
inline void add(int u,int v){
to[++el]=v;nxt[el]=head[u];head[u]=el;
}
void dfs1(int u,int f){
dep[u]=dep[fa[u]=f]+;
sz[u]=;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(v==f) continue;
dfs1(v,u);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int topf){
top[u]=topf;
id[dfn[u]=++dfs_clock]=u;
if(son[u]) dfs2(son[u],topf);
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(v==fa[u] || v==son[u]) continue;
dfs2(v,v);
}
}
inline void cover(int o,int l,int r,int v){
tag[o]=v;
nd[o].sum=v*(r-l+);
if(v>) nd[o].lmax=nd[o].rmax=nd[o].amax=nd[o].sum;
else nd[o].lmax=nd[o].rmax=nd[o].amax=;
}
inline void pushdown(int o,int l,int r){
if(tag[o]==-INF) return;
int mid=(l+r)>>;
cover(lson,tag[o]);cover(rson,tag[o]);
tag[o]=-INF;
}
void build(int o,int l,int r){
tag[o]=-INF;
if(l==r){
nd[o].sum=w[id[l]];
nd[o].lmax=nd[o].rmax=nd[o].amax=max(,w[id[l]]);
return;
}
int mid=(l+r)>>;
build(lson);build(rson);
nd[o]=nd[o<<]+nd[o<<|];
}
void update(int o,int l,int r,int ql,int qr,int v){
if(l>=ql && r<=qr) return cover(o,l,r,v);
int mid=(l+r)>>;
pushdown(o,l,r);
if(mid>=ql) update(lson,ql,qr,v);
if(mid<qr) update(rson,ql,qr,v);
nd[o]=nd[o<<]+nd[o<<|];
}
node query(int o,int l,int r,int ql,int qr){
if(l>=ql && r<=qr) return nd[o];
int mid=(l+r)>>;
pushdown(o,l,r);
if(mid<ql) return query(rson,ql,qr);
if(mid>=qr) return query(lson,ql,qr);
return query(lson,ql,qr)+query(rson,ql,qr);
}
void chain_update(int u,int v,int x){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
update(,,n,dfn[top[u]],dfn[u],x);
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
update(,,n,dfn[u],dfn[v],x);
}
int chain_query(int u,int v){
node un,vn;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v),swap(un,vn);
un=query(,,n,dfn[top[u]],dfn[u])+un;
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v),swap(un,vn);
vn=query(,,n,dfn[u],dfn[v])+vn;
swap(un.lmax,un.rmax);
return (un+vn).amax;
}
int main(){
n=read();
FOR(i,,n) w[i]=read();
FOR(i,,n-){
int u=read(),v=read();
add(u,v);add(v,u);
}
dfs1(,);dfs2(,);build(,,n);
m=read();
FOR(i,,m){
int op=read(),a=read(),b=read();
if(op==) chain_update(a,b,read());
else printf("%d\n",chain_query(a,b));
}
}
GSS7
后面的,留坑待填……