zoukankan      html  css  js  c++  java
  • SPOJ GSS系列

    众所周知的仅次于ynoi的毒瘤数据结构系列。(跟Qtree系列并列?)


    GSS1:

    长度为 $n$ 的序列 $a$,$m$ 个询问,每次询问区间 $[l,r]$ 之间的最大子段和。

    $1le n,mle 5 imes 10^4$。

    经典的线段树题。

    每个节点维护四个值:$sum,lmax,rmax,amax$。

    $sum$ 表示整个区间的和。

    $lmax$ 表示以 $l$ 为左端点的最大子段和。

    $rmax$ 表示以 $r$ 为右端点的最大子段和。

    $amax$ 表示整个的最大子段和。

    时间复杂度 $O(mlog n)$。

    具体可以看代码。(那是好久以前写的了,所以会有点丑)

    #include<bits/stdc++.h>
    using namespace std;
    const int maxm=144089;
    namespace Segment_Tree{
        struct node{
            int sum,lmax,rmax,amax;
        }e[maxm];
        int n;
        inline void pushup(int t){
            node ls=e[t<<1],rs=e[t<<1|1];
            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>>1;
            build_(l,mid,t<<1);
            build_(mid+1,r,t<<1|1);
            pushup(t);
        }
        void build(int x){
            n=x;
            build_(1,x,1);
        }
        node query_(int L,int R,int l,int r,int t){
            if(L>=l && R<=r) return e[t];
            int mid=L+R>>1;
            if(mid>=r) return query_(L,mid,l,r,t<<1);
            if(mid<l) return query_(mid+1,R,l,r,t<<1|1);
            node ans,ls,rs;
            ls=query_(L,mid,l,r,t<<1);rs=query_(mid+1,R,l,r,t<<1|1);
            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_(1,n,l,r,1).amax;
        }
    }
    int n,m;
    int main(){
        scanf("%d",&n);
        Segment_Tree::build(n);
        scanf("%d",&m);
        for(int i=1;i<=m;i++){
            int l,r;
            scanf("%d%d",&l,&r);
            printf("%d
    ",Segment_Tree::query(l,r));
        }
    }
    GSS1

    GSS2:

    长度为 $n$ 的序列 $a$,$m$ 个询问,每次询问区间 $[l,r]$ 之间的最大子段和。可以选空子段。注意此处的子段和是:如果一个数出现了多次,那么只算一次。

    $1le n,mle 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=100010;
    #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=0,f=0;
        while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
        while(ch>='0' && ch<='9') x=x*10+ch-'0',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*2],a[maxn];
    ll ans[maxn],sum[maxn*4],hmax[maxn*4],tag[maxn*4],htag[maxn*4];
    void pushup(int o){
        sum[o]=max(sum[o<<1],sum[o<<1|1]);
        hmax[o]=max(hmax[o<<1],hmax[o<<1|1]);
    }
    void pushdown(int o){
        hmax[o<<1]=max(hmax[o<<1],sum[o<<1]+htag[o]);
        hmax[o<<1|1]=max(hmax[o<<1|1],sum[o<<1|1]+htag[o]);
        sum[o<<1]+=tag[o];
        sum[o<<1|1]+=tag[o];
        htag[o<<1]=max(htag[o<<1],tag[o<<1]+htag[o]);
        htag[o<<1|1]=max(htag[o<<1|1],tag[o<<1|1]+htag[o]);
        tag[o<<1]+=tag[o];
        tag[o<<1|1]+=tag[o];
        tag[o]=htag[o]=0;
    }
    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)>>1;
        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)>>1;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,1,n) a[i]=read();
        q=read();
        FOR(i,1,q) qqq[i].l=read(),qqq[i].r=read(),qqq[i].id=i;
        sort(qqq+1,qqq+q+1);
        int curr=1;
        FOR(i,1,q){
            while(curr<=qqq[i].r){
                update(1,1,n,pre[a[curr]+100000]+1,curr,a[curr]);
                pre[a[curr]+100000]=curr;
                curr++;
            }
            ans[qqq[i].id]=query(1,1,n,qqq[i].l,qqq[i].r);
        }
        FOR(i,1,q) printf("%lld
    ",ans[i]);
    }
    GSS2

    GSS3:

    长度为 $n$ 的序列 $a$,$m$ 个操作:单点修改,或询问区间的最大子段和。

    $1le n,mle 5 imes 10^4$。

    跟GSS1一样,不说了。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=50050;
    #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=0,f=0;
        while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
        while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
        return f?-x:x;
    }
    struct node{
        int sum,lmax,rmax,amax;
    }nd[maxn*4];
    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)>>1;
        build(lson);
        build(rson);
        pushup(nd[o],nd[o<<1],nd[o<<1|1]);
    }
    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)>>1;
        if(mid>=p) update(lson,p,x);
        else update(rson,p,x);
        pushup(nd[o],nd[o<<1],nd[o<<1|1]);
    }
    node query(int o,int l,int r,int ql,int qr){
        if(l>=ql && r<=qr) return nd[o];
        int mid=(l+r)>>1;
        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,1,n) a[i]=read();
        build(1,1,n);
        q=read();
        FOR(i,1,q){
            int op=read(),l=read(),r=read();
            if(op) printf("%d
    ",query(1,1,n,l,r).amax);
            else update(1,1,n,l,r);
        }
    }
    GSS3

    GSS4:

    长度为 $n$ 的正整数序列 $a$,$m$ 个操作:区间开方(下取整),区间求和。

    $1le n,mle 10^5,sum a_ile 10^{18}$。

    这应该也是一个套路了……

    我们发现一个数 $x$ 开方下取整约 $loglog x$ 次之后便会变成 $1$。变成 $1$ 之后,再开方都不会对答案有影响。

    (你问我为什么是 $log log x$?因为每开一次方实际上就是把 $log x$ 除以 $2$,这样操作一共就要 $log log x$ 次)

     那么就上线段树,维护区间和和区间是否都为 $1$(bool)。

    修改时,对每个叶子结点都暴力修改。注意的是如果到了一个区间,区间都是 $1$,就可以跳过这个区间,因为修不修改不影响答案。

    由于一个叶子至多被暴力 $loglog a_i$ 次,所以时间复杂度是 $O(qlog nloglog a_i)$。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=100010;
    #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=0,f=0;
        while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
        while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
        return f?-x:x;
    }
    int t,n,q;
    ll a[maxn],sum[maxn*4];
    bool all1[maxn*4];
    inline void pushup(int o){
        sum[o]=sum[o<<1]+sum[o<<1|1];
        all1[o]=all1[o<<1]&all1[o<<1|1];
    }
    void build(int o,int l,int r){
        if(l==r) return void((sum[o]=a[l],all1[o]=(a[l]==1)));
        int mid=(l+r)>>1;
        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]==1)));
        int mid=(l+r)>>1;
        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)>>1;ll s=0;
        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:
    ",++t);
            FOR(i,1,n) a[i]=read();
            build(1,1,n);
            q=read();
            FOR(i,1,q){
                int op=read(),l=read(),r=read();
                if(l>r) swap(l,r);
                if(op) printf("%lld
    ",query(1,1,n,l,r));
                else update(1,1,n,l,r);
            }
            puts("");
        }
    }
    GSS4

    GSS5:

    长度为 $n$ 的正整数序列 $a$,$m$ 个询问,每次询问左端点在 $[x1,y1]$,右端点在 $[x2,y2]$ 的所有子段的和的最大值。

    $1le n,mle 10^4,x1le y1,x2le y2,x1le x2,y1le 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(mlog n)$。

    代码中写得有一点点不一样,最重要的还是理解。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=10010;
    #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=0,f=0;
        while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
        while(ch>='0' && ch<='9') x=x*10+ch-'0',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*4];
    int t,n,m,a[maxn];
    inline void pushup(int o){
        nd[o]=nd[o<<1]+nd[o<<1|1];
    }
    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)>>1;
        build(lson);build(rson);
        pushup(o);
    }
    node query(int o,int l,int r,int ql,int qr){
        if(ql>qr) return (node){0,0,0,0};
        if(l>=ql && r<=qr) return nd[o];
        int mid=(l+r)>>1;
        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,1,n) a[i]=read();
            build(1,1,n);
            m=read();
            printf("m=%d
    ",m);
            FOR(i,1,m){
                int l1=read(),r1=read(),l2=read(),r2=read();
                if(r1<l2){
                    node n1=query(1,1,n,l1,r1),n2=query(1,1,n,r1+1,l2-1),n3=query(1,1,n,l2,r2);
                    printf("%d
    ",n1.rmax+n2.sum+n3.lmax);
                }
                else{
                    node n1=query(1,1,n,l2,r1);
                    int ans=n1.amax;
                    node n2=query(1,1,n,l1,l2-1),n3=query(1,1,n,l2,r2);
                    ans=max(ans,n2.rmax+n3.lmax);
                    node n4=query(1,1,n,l1,r1),n5=query(1,1,n,r1+1,r2);
                    printf("%d
    ",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]$ 的最大子段和

    $1le n,mle 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=200020;
    #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=0,f=0;
        while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
        while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
        return f?-x:x;
    }
    int n,a[maxn],m,cnt,rt,fa[maxn],ch[maxn][2],sz[maxn],val[maxn],sum[maxn],lmax[maxn],rmax[maxn],amax[maxn];
    void pushup(int x){
        sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;
        sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x];
        if(!ch[x][0] && !ch[x][1]) lmax[x]=rmax[x]=amax[x]=val[x];
        else if(!ch[x][1]){
            lmax[x]=max(lmax[ch[x][0]],sum[ch[x][0]]+val[x]);
            rmax[x]=max(val[x],rmax[ch[x][0]]+val[x]);
            amax[x]=max(val[x],max(rmax[ch[x][0]]+val[x],amax[ch[x][0]]));
        }
        else if(!ch[x][0]){
            lmax[x]=max(val[x],val[x]+lmax[ch[x][1]]);
            rmax[x]=max(rmax[ch[x][1]],sum[ch[x][1]]+val[x]);
            amax[x]=max(val[x],max(val[x]+lmax[ch[x][1]],amax[ch[x][1]]));
        }
        else{
            lmax[x]=max(lmax[ch[x][0]],max(sum[ch[x][0]]+val[x],sum[ch[x][0]]+val[x]+lmax[ch[x][1]]));
            rmax[x]=max(rmax[ch[x][1]],max(sum[ch[x][1]]+val[x],sum[ch[x][1]]+val[x]+rmax[ch[x][0]]));
            amax[x]=max(val[x],max(amax[ch[x][0]],max(amax[ch[x][1]],max(rmax[ch[x][0]]+val[x],max(lmax[ch[x][1]]+val[x],rmax[ch[x][0]]+val[x]+lmax[ch[x][1]])))));
        }
    }
    void rotate(int x){
        int y=fa[x],z=fa[y],t=ch[y][1]==x,tt=ch[z][1]==y;
        int B=ch[x][t^1];
        fa[B]=y;ch[y][t]=B;
        fa[y]=x;ch[x][t^1]=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][1]==x,tt=ch[z][1]==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][0]]+1;
            if(x==s) return now;
            if(x<s) now=ch[now][0];
            else x-=s,now=ch[now][1];
        }
    }
    int build(int f,int l,int r){
        int mid=(l+r)>>1,tmp=++cnt;
        fa[tmp]=f;
        val[tmp]=a[mid];
        if(l<mid) ch[tmp][0]=build(tmp,l,mid-1);
        if(mid<r) ch[tmp][1]=build(tmp,mid+1,r);
        pushup(tmp);
        return tmp;
    }
    void insert(int p,int x){
        int l=kth(p),r=kth(p+1);
        splay(l,0);splay(r,l);
        int &nd=ch[ch[rt][1]][0];
        nd=++cnt;
        fa[nd]=ch[rt][1];
        val[nd]=x;
        pushup(nd);pushup(ch[rt][1]);pushup(rt);
    }
    void remove(int p){
        int l=kth(p),r=kth(p+2);
        splay(l,0);splay(r,l);
        ch[ch[rt][1]][0]=0;
        pushup(ch[rt][1]);pushup(rt);
    }
    void replace(int p,int x){
        int l=kth(p),r=kth(p+2);
        splay(l,0);splay(r,l);
        val[ch[ch[rt][1]][0]]=x;
        pushup(ch[ch[rt][1]][0]);pushup(ch[rt][1]);pushup(rt);
    }
    int query(int l,int r){
        l=kth(l);r=kth(r+2);
        splay(l,0);splay(r,l);
        return amax[ch[ch[rt][1]][0]];
    }
    int main(){
        n=read();
        FOR(i,1,n) a[i]=read();
        rt=build(0,0,n+1);
        m=read();
        FOR(i,1,m){
            char op[5];
            scanf("%s",op);
            int x=read();
            switch(op[0]){
                case 'I':insert(x,read());break;
                case 'D':remove(x);break;
                case 'R':replace(x,read());break;
                case 'Q':printf("%d
    ",query(x,read()));break;
            }
        }
    }
    GSS6

    GSS7:

    给定一棵树,有 $n$ 个节点,每一个节点都有一个权值 $x_i$。

    你需要执行 $q$ 次操作:

    1. 1 a b 查询 $(a,b)$ 这条链上的最大子段和,可以为空
    2. 2 a b c 将 $(a,b)$ 这条链的所有点权变为 $c$

    $1le n,qle 10^5,|x_i|,|c|le 10^4$。

    裸的树剖,只不过查询时会比较麻烦。

    我们可以先求出 $lca$ 到 $a$ 的最大子段和和 $lca$ 到 $b$ 的最大子段和。

    具体求的话,就是可以将在上面的一条重链与下面已有的答案接起来。

    最后把两个合并一下就好了。注意其中一个需要翻转后再拼。

    具体看代码。(一个裸的树剖为什么比splay还长)

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=100010,INF=2147483647;
    #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=0,f=0;
        while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
        while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
        return f?-x:x;
    }
    int n,m,w[maxn],el,head[maxn],to[maxn*2],nxt[maxn*2];
    int fa[maxn],dep[maxn],sz[maxn],son[maxn],top[maxn],dfs_clock,id[maxn],dfn[maxn];
    int tag[maxn*4];
    struct node{
        int sum,lmax,rmax,amax;
        node():sum(0),lmax(0),rmax(0),amax(0){}
        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*4];
    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]+1;
        sz[u]=1;
        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+1);
        if(v>0) nd[o].lmax=nd[o].rmax=nd[o].amax=nd[o].sum;
        else nd[o].lmax=nd[o].rmax=nd[o].amax=0;
    }
    inline void pushdown(int o,int l,int r){
        if(tag[o]==-INF) return;
        int mid=(l+r)>>1;
        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(0,w[id[l]]);
            return;
        }
        int mid=(l+r)>>1;
        build(lson);build(rson);
        nd[o]=nd[o<<1]+nd[o<<1|1];
    }
    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)>>1;
        pushdown(o,l,r);
        if(mid>=ql) update(lson,ql,qr,v);
        if(mid<qr) update(rson,ql,qr,v);
        nd[o]=nd[o<<1]+nd[o<<1|1];
    }
    node query(int o,int l,int r,int ql,int qr){
        if(l>=ql && r<=qr) return nd[o];
        int mid=(l+r)>>1;
        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(1,1,n,dfn[top[u]],dfn[u],x);
            u=fa[top[u]];
        }
        if(dep[u]>dep[v]) swap(u,v);
        update(1,1,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(1,1,n,dfn[top[u]],dfn[u])+un;
            u=fa[top[u]];
        }
        if(dep[u]>dep[v]) swap(u,v),swap(un,vn);
        vn=query(1,1,n,dfn[u],dfn[v])+vn;
        swap(un.lmax,un.rmax);
        return (un+vn).amax;
    }
    int main(){
        n=read();
        FOR(i,1,n) w[i]=read();
        FOR(i,1,n-1){
            int u=read(),v=read();
            add(u,v);add(v,u);
        }
        dfs1(1,0);dfs2(1,1);build(1,1,n);
        m=read();
        FOR(i,1,m){
            int op=read(),a=read(),b=read();
            if(op==2) chain_update(a,b,read());
            else printf("%d
    ",chain_query(a,b));
        }
    }
    GSS7

     后面的,留坑待填……

  • 相关阅读:
    cf1058c 暴力
    cf1058b 点是否在一个矩形里
    cf1058E 思维 前缀处理 位运算
    Codeforces Round #622 (Div. 2)C(单调栈,DP)
    Codeforces Round #623 (Div. 1, based on VK Cup 2019-2020
    Atcoder Beginner Contest 156E(隔板法,组合数学)
    【PAT甲级】1117 Eddington Number (25分)
    【PAT甲级】1116 Come on! Let's C (20分)
    【PAT甲级】1115 Counting Nodes in a BST (30分)(二叉查找树)
    Codeforces Round #621 (Div. 1 + Div. 2)E(二分查找,枚举分界点,容斥原理)
  • 原文地址:https://www.cnblogs.com/1000Suns/p/10369734.html
Copyright © 2011-2022 走看看