SPOJ GSS 1~8

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值