SPOJ GSS 1~8
来源:互联网 发布:mac导出iphone照片 编辑:程序博客网 时间:2024/06/05 18:13
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):0using 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):0using 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):0using 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):0using 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):0using 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;}
- SPOJ GSS 1~8
- SPOJ GSS
- spoj GSS系列
- SPOJ GSS系列
- spoj GSS系列
- 【SPOJ】GSS系列
- spoj GSS系列之GSS1 和 GSS3
- SPOJ GSS 1. Can you answer these queries I
- SPOJ GSS 5. Can you answer these queries V
- GSS 1 区间最大子段和
- SPOJ GSS1 - Can you answer these queries I(线段树维护GSS)
- SPOJ GSS系列 最大子段和 线段树+树链剖分+splay 1043 1557 1716 2713 2916 4487 6779
- GSS系列(1)——GSS1&&GSS3
- 怒水一记 GSS
- SPOJ 1
- 【安全】GSS-API详解
- 【SPOJ】QTREE 1 树链剖分裸题
- SPOJ QTree 1
- python数据结构学习笔记-2016-10-22-01-评价python列表
- C语言函数指针之回调函数
- 数组的定义
- 易错的指针问题
- lua笔记1
- SPOJ GSS 1~8
- NSString 各种类型的判断
- TCP/IP详解学习笔记(7)-广播和多播,IGMP协议
- do-while循环
- 正则表达式学习
- 设计模式之工厂模式
- jsp访问数据库(学生数据表)
- 螺旋线
- Cmake创建交叉编译应用(二)