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;}
0 0
原创粉丝点击