GSS是一系列查询区间最大子段和及其变种的题目。
GSS 1 ~ 8 的题号分别是:1043 & 1557 & 1716 & 2713 & 2916 & 4487 & 6779 & 19543
目前完成进度:1 ~ 7(第8题不会做,暂时坑着,以后会了在做吧。。。)
维护区间最大字段和的思想:如果询问任何一段区间内的最大子段和 ,我们可以把这段区间通过一些分界线分成若干小区间,大区间的最大子段和要么是某个小区间的最大子段和,要么是经过某条分界线的左右最大子段和,这些常常用线段树维护,因为线段树访问区间是可以有序的。
GSS的这些题目都运用了这个思想。
SPOJ 1043 GSS 1 线段树
应该是不难的。我们假设一个区间的最大子段已经找到,往区间里随便放一条分界线。有两种情况:一是最大子段在分界线的某一边,二是最大子段经过分界线。前者可以变为小区间的子问题,后者可以通过小区间维护经过区间端点的最值来统计答案。注意到线段树对区间进行分界可以严格按照从左到右的顺序,于是线段树搞即可。
#include<cstdio>
#include<algorithm>
#define N 50005
#define cmax(_i,_j) (_i)<(_j)?(_i)=(_j):0
using namespace std;
struct seg
{
int l, r, lm, rm, mx, sum;
}t[N*10];
int a[N];
void build(int x, int l, int r)
{
t[x].l=l;
t[x].r=r;
if(l==r)
{
t[x].lm = t[x].mx = t[x].rm = t[x].sum = a[l];
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
t[x].sum = t[x<<1].sum + t[x<<1|1].sum;
t[x].lm = max(t[x<<1].lm, t[x<<1].sum+t[x<<1|1].lm);
t[x].rm = max(t[x<<1|1].rm, t[x<<1|1].sum+t[x<<1].rm);
t[x].mx = max(max(t[x<<1].mx, t[x<<1|1].mx), t[x<<1].rm+t[x<<1|1].lm);
}
int ans, pre;
void query(int x, int l, int r)
{
if(l <= t[x].l && t[x].r <= r)
{
cmax(ans,t[x].mx);
cmax(ans,pre + t[x].lm);
pre = max(pre+t[x].sum,t[x].rm);
return;
}
int mid=(t[x].l + t[x].r)>>1;
if(l<=mid)query(x<<1,l,r);
if(mid+1<=r)query(x<<1|1,l,r);
}
int main()
{
int n, m, l, r;
scanf("%d",&n);
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
build(1,1,n);
scanf("%d",&m);
for(int i = 1; i <= m; i++)
{
scanf("%d%d",&l,&r);
ans = a[l];
pre = 0;
query(1,l,r);
printf("%d\n",ans);
}
}
SPOJ 1557 GSS 2 线段树+离线排序
难想+难写的丧题
按照上一题的套路,肯定是用线段树。接着我猜了一些结论,结果都被自己推翻了,然后就往别的方向考虑,也没考虑出什么来。。。
暂时不管询问怎么样,我们先考虑题目中说的去重最大子段和怎么求。这题应该没有什么靠谱的贪心(反正我想的贪心都被自己推翻了),于是我们可以考虑从左到右一个数一个数来做。例如做到第i位,我们要求强制取a[i]。此时我们要新加入一个a[i],那么a[i]的贡献范围就应当是上一次出现a[i]的位置(记为j)到i位置这一段区间,于是我们可以在j+1~i之间全部加上a[i],表示左端点取这里时增加的贡献,这就用到了线段树。
这启发我们应当将询问离线,按右端点排序来做。
于是会有一个问题,询问并没有要求强制取右端点,如果一个一个在[l,r]上找,时间复杂度会严重退化。这时我们需要在最大值(v)和增量(lazy)的基础上多记两个标记:历史最大值(mx_v)、历史最大增量(mx_lazy)。然后就可以维护了。
具体的维护方法有点复杂,反正我写了挺久。。。毕竟我弱。。。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define ll long long
#define cmax(u,v) (u)<(v)?(u)=(v):0
using namespace std;
int a[N], pos[2*N];
ll ans[N];
struct seg
{
int l, r;
ll v, mx_v, lazy, mx_lazy;
}t[N*10];
struct que
{
int l, r, id;
bool operator < (que a) const
{
return r < a.r;
}
}q[N];
void build(int x, int l, int r)
{
t[x] = (seg){l,r,0,0,0,0};
if(l==r)
{
return;
}
int mid = (l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
}
void pushdown(int x)
{
int lson = x<<1, rson = x<<1|1;
cmax(t[lson].mx_lazy, t[lson].lazy + t[x].mx_lazy);
cmax(t[rson].mx_lazy, t[rson].lazy + t[x].mx_lazy);
cmax(t[lson].mx_v, t[lson].v + t[x].mx_lazy);
cmax(t[rson].mx_v, t[rson].v + t[x].mx_lazy);
t[lson].v += t[x].lazy;
t[rson].v += t[x].lazy;
t[lson].lazy += t[x].lazy;
t[rson].lazy += t[x].lazy;
t[x].lazy = t[x].mx_lazy = 0;
}
void update(int x, int l, int r, ll v)
{
pushdown(x);
if(l <= t[x].l && t[x].r <= r)
{
t[x].lazy += v;
cmax(t[x].mx_lazy, t[x].lazy);
cmax(t[x].mx_v, t[x].v + t[x].mx_lazy);
t[x].v += v;
return;
}
int mid = (t[x].l + t[x].r)>>1;
if(l<=mid)update(x<<1,l,r,v);
if(mid<r)update(x<<1|1,l,r,v);
t[x].v = max(t[x<<1].v, t[x<<1|1].v);
t[x].mx_v = max(t[x<<1].mx_v, t[x<<1|1].mx_v);
}
ll query(int x, int l, int r)
{
pushdown(x);
if(l <= t[x].l && t[x].r <= r)
{
return t[x].mx_v;
}
int mid = (t[x].l + t[x].r)>>1;
ll p1 = 0, p2 = 0;
if(l<=mid)p1 = query(x<<1,l,r);
if(mid<r)p2 = query(x<<1|1,l,r);
return max(p1,p2);
}
void clr()
{
memset(pos,0,sizeof(pos));
}
int main()
{
int n, m;
while(scanf("%d",&n) == 1)
{
clr();
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
scanf("%d",&m);
for(int i = 1; i <= m; i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id = i;
}
sort(q+1,q+1+m);
build(1,1,n);
int cur = 1;
for(int i = 1; i <= n && cur <= m; i++)
{
update(1,pos[a[i]+N]+1,i,a[i]);
pos[a[i]+N]=i;
while(q[cur].r == i)
{
ans[q[cur].id] = query(1,q[cur].l,q[cur].r);
++cur;
if(cur>m)break;
}
}
for(int i = 1; i <= m; i++)
printf("%lld\n",ans[i]);
}
}
SPOJ 1716 GSS 3 线段树
不就是GSS1加了修改吗,没啥好说的。。。
#include<cstdio>
#include<algorithm>
#define N 50005
#define cmax(_i,_j) (_i)<(_j)?(_i)=(_j):0
using namespace std;
struct seg
{
int l, r, lm, rm, mx, sum;
}t[N*10];
int a[N];
void build(int x, int l, int r)
{
t[x].l=l;
t[x].r=r;
if(l==r)
{
t[x].lm = t[x].mx = t[x].rm = t[x].sum = a[l];
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
t[x].sum = t[x<<1].sum + t[x<<1|1].sum;
t[x].lm = max(t[x<<1].lm, t[x<<1].sum+t[x<<1|1].lm);
t[x].rm = max(t[x<<1|1].rm, t[x<<1|1].sum+t[x<<1].rm);
t[x].mx = max(max(t[x<<1].mx, t[x<<1|1].mx), t[x<<1].rm+t[x<<1|1].lm);
}
int ans, pre;
void query(int x, int l, int r)
{
if(l <= t[x].l && t[x].r <= r)
{
cmax(ans,t[x].mx);
cmax(ans,pre + t[x].lm);
pre = max(pre+t[x].sum,t[x].rm);
return;
}
int mid=(t[x].l + t[x].r)>>1;
if(l<=mid)query(x<<1,l,r);
if(mid+1<=r)query(x<<1|1,l,r);
}
void update(int x, int pos, int v)
{
if(t[x].l == t[x].r)
{
t[x].lm = t[x].mx = t[x].rm = t[x].sum = a[pos] = v;
return;
}
int mid = (t[x].l+t[x].r)>>1;
if(pos<=mid)update(x<<1,pos,v);
else update(x<<1|1,pos,v);
t[x].sum = t[x<<1].sum + t[x<<1|1].sum;
t[x].lm = max(t[x<<1].lm, t[x<<1].sum + t[x<<1|1].lm);
t[x].rm = max(t[x<<1|1].rm, t[x<<1|1].sum + t[x<<1].rm);
t[x].mx = max(max(t[x<<1].mx, t[x<<1|1].mx), t[x<<1].rm+t[x<<1|1].lm);
}
int main()
{
int n, m, l, r, opt;
scanf("%d",&n);
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
build(1,1,n);
scanf("%d",&m);
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d",&opt,&l,&r);
if(opt)
{
ans = a[l];
pre = 0;
query(1,l,r);
printf("%d\n",ans);
}
else
{
update(1,l,r);
}
}
}
SPOJ 2713 GSS 4 链表+树状数组 或 线段树
刚开始脑洞大开,想到矩阵乘法去了,然而并没有什么卵用。
仔细观察,开平方有什么特殊性质?数字减少得特别快!手算可以知道,就算是10^18,连续开平方也只能开六七次。于是我们知道总的有效操作次数肯定是O(n)的,那么唯一要解决的问题就是如何在每一次都能进行有效操作。
如果数字为1或0,那么如何开平方都不是有效操作,我们应当把这个数字忽略掉,然后我就想到了链表,如果一个数为1或0,就把它从链表上拆下来,这样一定可以不重不漏地做到所有的有效操作。
注意链表的起点不是nxt[l-1]!因为l-1可能已经被拆了,所以这样的复杂度是可能严重退化的,我们需要用类似并查集的思想来维护nxt数组。外面套一个树状数组(线段树太长了不想打)即可。
后来看了一下,网上的做法是直接给无法进行有效操作的区间打上标记,不再访问。实际上思想是一样的,虽然这样复杂度有保证,但还是有会浪费一些多余的操作。然而我的代码不仅基本没有多余操作,而且短,重要的是常数小!!!
#include<cmath>
#include<cstdio>
#include<cstring>
#define N 100005
#define ll long long
#define lowbit(_i) (_i&-_i)
using namespace std;
bool vis[N];
int n, m, pre[N], nxt[N];
ll c[N], a[N];
ll ask(int x){ll ret = 0; while(x){ret += c[x]; x -= lowbit(x);} return ret;}
void upd(int x, ll v){while(x <= n){c[x] += v; x += lowbit(x);}}
void clr()
{
memset(c,0,sizeof(c));
memset(vis,0,sizeof(vis));
}
int find(int x)
{
if(!vis[x])return x;
else return nxt[x]=find(nxt[x]);
}
int main()
{
int kase=0;
while(scanf("%d",&n) == 1)
{
clr();
printf("Case #%d:\n",++kase);
for(int i = 1; i <= n; i++)
{
scanf("%lld",&a[i]);
upd(i,a[i]);
pre[i]=i-1;
nxt[i]=i+1;
}
nxt[0]=1;
nxt[n]=n+1;
scanf("%d",&m);
for(int i = 1, opt, l, r; i <= m; i++)
{
scanf("%d%d%d",&opt,&l,&r);
if(l>r)
{
int tmp = l;
l = r;
r = tmp;
}
if(opt==1)
printf("%lld\n",ask(r)-ask(l-1));
else
{
for(int i = find(l); i <= r; i = nxt[i])
{
ll tmp = a[i];
tmp -= (a[i]=(ll)sqrt(a[i]+0.5));
upd(i,-tmp);
if(a[i] < 2)
{
nxt[pre[i]]=nxt[i];
pre[nxt[i]]=pre[i];
vis[i]=1;
}
}
}
}
puts("");
}
}
SPOJ 2916 GSS 5 线段树
最多只有两种情况。[x1,y1],[x2,y2]这两个区间要么相交,要么不相交。
对于不相交的情况,我们只需要分别计算强制取y1向左扩展的最大值和强制取x2向右扩展的最大值加上中间的值即可,处理方法同GSS1线段树。
对于相交的情况,分三类讨论:穿过x2、穿过y1、处于[x2,y1]之间。处理方法也是大同小异。不过对于分界线的处理一定要仔细,否则就会像我一样,自信地交上去,然后狂WA不止。。。
#include<cstdio>
#include<algorithm>
#define N 100005
#define cmax(u,v) (u)<(v)?(u)=(v):0
using namespace std;
namespace ziqian
{
const int INF = 1<<29;
int a[N], L, R, tmp;
struct seg
{
int l, r, lm, mx, rm, sum;
}t[N*5];
void build(int x, int l, int r)
{
t[x].l=l;
t[x].r=r;
if(l==r)
{
t[x].lm = t[x].rm = t[x].mx = t[x].sum = a[l];
return;
}
int mid = (l+r)>>1, lson = x<<1, rson = x<<1|1;
build(lson,l,mid);
build(rson,mid+1,r);
t[x].sum = t[lson].sum + t[rson].sum;
t[x].lm = max(t[lson].lm, t[lson].sum+t[rson].lm);
t[x].rm = max(t[rson].rm, t[rson].sum+t[lson].rm);
t[x].mx = max(max(t[lson].mx, t[rson].mx), t[lson].rm + t[rson].lm);
}
int query_sum(int x, int l, int r)
{
if(l>r)return 0;
if(l <= t[x].l && t[x].r <= r)
return t[x].sum;
int mid = (t[x].l+t[x].r)>>1, ret = 0;
if(l<=mid)ret+=query_sum(x<<1,l,r);
if(mid<r)ret+=query_sum(x<<1|1,l,r);
return ret;
}
void query_lm(int x, int l, int r)
{
if(l>r)return;
if(l<=t[x].l && t[x].r<=r)
{
cmax(tmp,L+t[x].lm);
L += t[x].sum;
return;
}
int mid=(t[x].l+t[x].r)>>1;
if(l<=mid)query_lm(x<<1,l,r);
if(mid<r)query_lm(x<<1|1,l,r);
}
void query_rm(int x, int l, int r)
{
if(l>r)return;
if(l<=t[x].l && t[x].r<=r)
{
cmax(tmp,R+t[x].rm);
R += t[x].sum;
return;
}
int mid=(t[x].l+t[x].r)>>1;
if(mid<r)query_rm(x<<1|1,l,r);
if(l<=mid)query_rm(x<<1,l,r);
}
void query_mx(int x, int l, int r)
{
if(l>r)return;
if(l<=t[x].l && t[x].r<=r)
{
cmax(tmp,t[x].lm + R);
cmax(tmp,t[x].mx);
R = max(R+t[x].sum, t[x].rm);
return;
}
int mid = (t[x].l+t[x].r)>>1;
if(l<=mid)query_mx(x<<1,l,r);
if(mid<r)query_mx(x<<1|1,l,r);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n, m;
scanf("%d",&n);
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
build(1,1,n);
scanf("%d",&m);
for(int i = 1, x1, y1, x2, y2; i <= m; i++)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
if(y1<x2)
{
int ans = 0;
ans += query_sum(1,y1+1,x2-1);
tmp=-INF;R=0;query_rm(1,x1,y1);ans += tmp;
tmp=-INF;L=0;query_lm(1,x2,y2);ans += tmp;
printf("%d\n",ans);
}
else
{
int p1=0, p2=0, p3;
tmp=-INF;R=0;query_rm(1,x1,x2-1); p1 += tmp;
tmp=-INF;L=0;query_lm(1,x2,y2); p1 += tmp;
tmp=-INF;R=0;query_rm(1,x1,y1); p2 += tmp;
tmp=-INF;L=0;query_lm(1,y1+1,y2); p2 += tmp;
tmp=-INF;R=0;query_mx(1,x2,y1); p3 = tmp;
printf("%d\n",max(max(p1,p2),p3));
}
}
}
return 0;
}
}
int main()
{
ziqian::main();
}
SPOJ 4487 GSS 6 splay
如果没有插入操作的话,线段树依然可以维护。既然有了插入操作,那我们就可以用splay来维护,维护方法类似于线段树,对于询问区间[l,r],就先把l-1旋转到根,把r+1旋转到根底下,那么r+1的左子树就是所求区间了。
#include<cstdio>
#include<algorithm>
#define N 100005
#define max3(u,v,w) max(max(u,v),w)
using namespace std;
int a[N];
namespace ziqian
{
char ss[5];
int n, m;
const int INF = 1<<28;
struct node
{
node *ch[2], *fa;
int lm, rm, mx, sum, siz, val;
}t[N*2], *null;
struct Splay_Tree
{
int tot;
node *root;
void init(int x)
{
tot = 0;
null = &t[++tot];
null->fa = null->ch[0] = null->ch[1] = null;
null->lm = null->rm = null->mx = null->val = -INF;
null->sum = 0;
null->siz = 0;
root = &t[++tot];
root->fa = root->ch[0] = root->ch[1] = null;
root->lm = root->rm = root->mx = root->sum = root->val = x;
root->siz = 1;
}
int type(node *p)
{
return p->fa->ch[0] == p ? 0 : 1;
}
void push_up(node *p)
{
p->siz = p->ch[0]->siz + p->ch[1]->siz + 1;
p->sum = p->ch[0]->sum + p->ch[1]->sum + p->val;
p->mx = max3(max(p->ch[0]->mx, p->ch[1]->mx), max(p->ch[0]->rm+p->val, p->ch[1]->lm+p->val), max(p->val,p->val+p->ch[0]->rm+p->ch[1]->lm));
p->lm = max3(p->ch[0]->lm, p->ch[0]->sum + p->val, p->ch[0]->sum + p->val + p->ch[1]->lm);
p->rm = max3(p->ch[1]->rm, p->ch[1]->sum + p->val, p->ch[1]->sum + p->val + p->ch[0]->rm);
}
void rotate(node *p)
{
node *f = p->fa, *g = f->fa, *c = p->ch[type(p)^1];
int f1 = type(p), f2 = type(f);
if(g!=null)
g->ch[f2] = p;
if(c!=null)
c->fa = f;
p->fa=g;
p->ch[f1^1] = f;
f->fa=p;
f->ch[f1] = c;
push_up(f);
}
void splay(node *p, node *goal)
{
if(goal == null)
root = p;
while(p->fa != goal)
{
node *f = p->fa;
if(f->fa == goal)rotate(p);
else
{
if(type(p) == type(f))
{
rotate(f);
rotate(p);
}
else
{
rotate(p);
rotate(p);
}
}
}
push_up(p);
}
node* select(int k)
{
k--;
node *p = root;
while(p->ch[0]->siz != k)
{
if(p->ch[0]->siz > k)
p=p->ch[0];
else
{
k -= 1+p->ch[0]->siz;
p=p->ch[1];
}
}
return p;
}
void newnode(node *f, int pos, int val)
{
node *p = &t[++tot];
f->ch[pos] = p;
p->ch[0] = p->ch[1] = null;
p->fa = f;
p->lm = p->mx = p->rm = p->sum = p->val = val;
p->siz = 1;
}
void insert(int pos, int val)
{
node *p;
if(pos == 0)
{
p = select(1);
splay(p,null);
newnode(p,0,val);
push_up(root);
return;
}
p = select(pos);
splay(p,null);
if(p->ch[1]==null)
{
newnode(p,1,val);
push_up(p);
p = p->ch[1];
}
else
{
splay(select(pos+1),p);
newnode(p->ch[1],0,val);
push_up(p->ch[1]);
push_up(p);
p = p->ch[1]->ch[0];
}
splay(p,null);
}
void del(int pos)
{
node *p, *pp;
if(pos == 1)
{
p = select(2);
splay(p,null);
root->ch[0]=null;
push_up(root);
}
else if(pos == n)
{
p = select(n-1);
splay(p,null);
root->ch[1]=null;
push_up(root);
}
else
{
p = select(pos-1);
pp = select(pos+1);
splay(p,null);
splay(pp,p);
pp->ch[0]=null;
push_up(pp);
push_up(p);
}
}
void replace(int pos, int val)
{
node *p = select(pos);
splay(p,null);
p->val = val;
push_up(p);
}
int query(int l, int r)
{
node *p, *pp;
if(l==1 && r==n)
{
p = select(1);
splay(p,null);
return root->mx;
}
else if(l==1)
{
p = select(r+1);
splay(p,null);
return root->ch[0]->mx;
}
else if(r==n)
{
p = select(l-1);
splay(p,null);
return p->ch[1]->mx;
}
else
{
p = select(l-1);
pp = select(r+1);
splay(p,null);
splay(pp,p);
return pp->ch[0]->mx;
}
}
}s;
int main()
{
scanf("%d",&n);
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
s.init(a[1]);
for(int i = 2; i <= n; i++)
s.insert(i-1,a[i]);
scanf("%d",&m);
for(int i = 1, x, y; i <= m; i++)
{
scanf("%s",ss);
if(ss[0]=='I')
{
scanf("%d%d",&x,&y);
--x;
s.insert(x,y);
++n;
}
else if(ss[0]=='D')
{
scanf("%d",&x);
s.del(x);
--n;
}
else if(ss[0]=='R')
{
scanf("%d%d",&x,&y);
s.replace(x,y);
}
else
{
scanf("%d%d",&x,&y);
int ans = s.query(x,y);
printf("%d\n",ans);
}
}
return 0;
}
}
int main()
{
ziqian::main();
}
SPOJ 6779 GSS 7 树链剖分+线段树
我们可以把询问(a,b)之间的路径看成一个序列,那么就又变成了类似GSS3的经典问题。我们可以用树剖+线段树维护这些序列,依然利用分界线思想来找最大子段和。
#include<cstdio>
#include<algorithm>
#define N 100005
#define cmax(u,v) (u)<(v)?(u)=(v):0
using namespace std;
namespace ziqian
{
struct seg{int l, r, lm, rm, mx, sum, lazy;}t[N*5];
struct edge{int next,to,val;}e[N<<1];
const int INF = 1<<29;
int ecnt=1, tcnt, ans;
int last[N], v[N], top[N], fa[N], siz[N], son[N], dep[N], pos[N], repos[N], q[N], tp[N], R, L;
void addedge(int a, int b)
{
e[++ecnt]=(edge){last[a],b};
last[a]=ecnt;
}
void dfs1(int x)
{
dep[x]=dep[fa[x]]+1;
siz[x]=1;
for(int i = last[x]; i; i=e[i].next)
{
int y=e[i].to;
if(y==fa[x])continue;
fa[y]=x;
dfs1(y);
siz[x]+=siz[y];
if(siz[y] > siz[son[x]])
son[x]=y;
}
}
void dfs2(int x)
{
pos[x]=++tcnt;
repos[tcnt]=x;
if(son[fa[x]]==x)top[x]=top[fa[x]];
else top[x]=x;
if(son[x])dfs2(son[x]);
for(int i = last[x]; i; i=e[i].next)
{
int y=e[i].to;
if(y==fa[x] || y==son[x])continue;
dfs2(y);
}
}
void pushup(int x)
{
int lson = x<<1, rson = x<<1|1;
t[x].lm = max(t[lson].lm, t[lson].sum + t[rson].lm);
t[x].rm = max(t[rson].rm, t[rson].sum + t[lson].rm);
t[x].sum = t[lson].sum + t[rson].sum;
t[x].mx = max(max(t[lson].mx, t[rson].mx), t[lson].rm + t[rson].lm);
}
void pushdown(int x)
{
if(t[x].lazy == INF)return;
int lson = x<<1, rson = x<<1|1;
t[lson].lazy = t[rson].lazy = t[x].lazy;
t[lson].sum = (t[lson].r-t[lson].l+1)*t[x].lazy;
t[rson].sum = (t[rson].r-t[rson].l+1)*t[x].lazy;
t[lson].lm = t[lson].rm = t[lson].mx = t[x].lazy>0?t[lson].sum:t[x].lazy;
t[rson].lm = t[rson].rm = t[rson].mx = t[x].lazy>0?t[rson].sum:t[x].lazy;
t[x].lazy = INF;
}
void build(int x, int l, int r)
{
t[x].l = l;
t[x].r = r;
t[x].lazy = INF;
if(l==r)
{
t[x].sum = t[x].lm = t[x].mx = t[x].rm = v[repos[l]];
return;
}
int mid = (l+r)>>1, lson = x<<1, rson = x<<1|1;
build(lson,l,mid);
build(rson,mid+1,r);
pushup(x);
}
void update(int x, int l, int r, int v)
{
if(l <= t[x].l && t[x].r <= r)
{
t[x].sum = (t[x].r-t[x].l+1)*v;
t[x].lm = t[x].rm = t[x].mx = v>0?t[x].sum:v;
t[x].lazy = v;
return;
}
pushdown(x);
int mid = (t[x].l+t[x].r)>>1;
if(l<=mid)update(x<<1,l,r,v);
if(mid<r)update(x<<1|1,l,r,v);
pushup(x);
}
int LCA(int a, int b)
{
while(top[a]!=top[b])
{
if(dep[top[a]] < dep[top[b]])swap(a,b);
a = fa[top[a]];
}
return dep[a] < dep[b] ? a : b;
}
void query(int x, int l ,int r, int &cur)
{
if(l <= t[x].l && t[x].r <= r)
{
cmax(ans, t[x].mx);
cmax(ans, cur + t[x].rm);
cur = max(t[x].sum + cur, t[x].lm);
return;
}
pushdown(x);
int mid = (t[x].l + t[x].r)>>1;
if(mid < r)query(x<<1|1,l,r,cur);
if(l <= mid)query(x<<1,l,r,cur);
}
int main()
{
int n, m;
scanf("%d",&n);
for(int i = 1; i <= n; i++)
scanf("%d",&v[i]);
for(int i = 1, a, b; i < n; i++)
{
scanf("%d%d",&a,&b);
addedge(a,b);
addedge(b,a);
}
dfs1(1);
dfs2(1);
build(1,1,n);
scanf("%d",&m);
for(int opt, a, b, c;m--;)
{
scanf("%d",&opt);
if(opt == 1)
{
scanf("%d%d",&a,&b);
int lca = LCA(a,b);
L = R = 0;
ans = -INF;
while(top[a] != top[lca])
{
query(1,pos[top[a]],pos[a],R);
a=fa[top[a]];
}
if(a!=lca)query(1,pos[lca]+1,pos[a],R);
while(top[b] != top[lca])
{
query(1,pos[top[b]],pos[b],L);
b=fa[top[b]];
}
query(1,pos[lca], pos[b],L);
cmax(ans,L+R);
cmax(ans,0);
printf("%d\n",ans);
}
else
{
scanf("%d%d%d",&a,&b,&c);
int lca = LCA(a,b);
for(int i = 1; i <= 2; i++)
{
int t = (i==1?a:b);
while(top[t] != top[lca])
{
update(1,pos[top[t]],pos[t],c);
t = fa[top[t]];
}
update(1,pos[lca],pos[t],c);
}
}
}
return 0;
}
}
int main()
{
ziqian::main();
return 0;
}