【bzoj2333 & luoguP3273】棘手的操作(线段树合并)

  题目传送门:bzoj2333 luoguP3273

  这操作还真“棘手”。。听说这题是可并堆题?然而我不会可并堆。于是我就写了线段数合并,然后调了一晚上,数据结构毁一生!!!QAQ……

  其实这题也可以把合并强行看成树上的关系然后dfs序后直接线段树的,然而我菜啊。。看到连边就只能想到线段树合并。

  首先用并查集维护图的连通性,然后对于每个连通块建一棵下标为点的编号的线段树,于是:

  U=合并两棵树;

  A1:单点加;

  A2:区间加;

  A3:因为是整体加,所以我们可以维护一个delta表示当前每个数被整体加了多少,然后输出时直接加上就好了;

  F1:单点查询;

  F2:区间查询;

  然而还有一个F3整体查询最大值很难处理。于是我再开了一颗线段树,维护每个连通块内的最大值,修改时顺便在上面修改信息,F3操作可以直接在树上查询。

  时间复杂度$ O(n log n) $,然而常数极大。

  两颗线段树+奇奇怪怪的维护方法,使写出来的代码膨胀到了3.6kB……然后,调试++,and then,(如果你写的不优美)MLE+TLE。经过了一番痛苦的调试后,终于过了。。。QAQ

  又臭又长的代码:

#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<algorithm>
#define ll long long
#define inf 0x3f3f3f3f
#define maxn 300010
inline ll read()
{
    ll x=0; char c=getchar(),f=1;
    for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1;
    for(;'0'<=c&&c<='9';c=getchar())x=x*10+c-'0';
    return x*f;
}
struct segment_tree1{
    struct point{
        int lc,rc,mx,delta;
    }sgt[20*maxn];
    int tot;
    void add(int now,int l,int r,int x,int y,int k)
    {
        if(x<=l&&r<=y)sgt[now].delta+=k,sgt[now].mx+=k;
        else{
            int mid=(l+r)>>1;
            if(x<=mid){
                if(!sgt[now].lc)sgt[now].lc=++tot;
                add(sgt[now].lc,l,mid,x,y,k);
            }
            if(mid<y){
                if(!sgt[now].rc)sgt[now].rc=++tot;
                add(sgt[now].rc,mid+1,r,x,y,k);
            }
            sgt[now].mx=std::max(sgt[sgt[now].lc].mx,sgt[sgt[now].rc].mx);
            if(sgt[now].mx!=-inf)sgt[now].mx+=sgt[now].delta;
        }
    }
    int getmax(int now,int l,int r,int x,int y)
    {
        if(x<=l&&r<=y)return sgt[now].mx;
        else{
            int mid=(l+r)>>1,mx=-inf;
            if(x<=mid&&sgt[now].lc)mx=std::max(mx,getmax(sgt[now].lc,l,mid,x,y));
            if(mid<y&&sgt[now].rc)mx=std::max(mx,getmax(sgt[now].rc,mid+1,r,x,y));
            return mx+sgt[now].delta;
        }
    }
    void merge(int x,int y,int d)
    {
        d+=sgt[y].delta-sgt[x].delta;
        if(!sgt[x].lc)sgt[x].lc=sgt[y].lc,sgt[sgt[x].lc].delta+=d,sgt[sgt[x].lc].mx+=d;
        else if(sgt[y].lc)merge(sgt[x].lc,sgt[y].lc,d);
        if(!sgt[x].rc)sgt[x].rc=sgt[y].rc,sgt[sgt[x].rc].delta+=d,sgt[sgt[x].rc].mx+=d;
        else if(sgt[y].rc)merge(sgt[x].rc,sgt[y].rc,d);
        sgt[x].mx=std::max(sgt[sgt[x].lc].mx,sgt[sgt[x].rc].mx)+sgt[x].delta;
    }
}t1;
struct segment_tree2{
    struct point{
        int mx,delta;
    }sgt[4*maxn];
    void add(int now,int l,int r,int x,int y,int k)
    {
        if(x<=l&&r<=y)sgt[now].delta+=k,sgt[now].mx+=k;
        else{
            int mid=(l+r)>>1;
            if(x<=mid)add(now<<1,l,mid,x,y,k);
            if(mid<y)add(now<<1|1,mid+1,r,x,y,k);
            sgt[now].mx=std::max(sgt[now<<1].mx,sgt[now<<1|1].mx);
            if(sgt[now].mx!=-inf)sgt[now].mx+=sgt[now].delta;
        }
    }
    int getmax(int now,int l,int r,int x,int y)
    {
        if(x<=l&&r<=y)return sgt[now].mx;
        else{
            int mid=(l+r)>>1,mx=-inf;
            if(x<=mid)mx=std::max(mx,getmax(now<<1,l,mid,x,y));
            if(mid<y)mx=std::max(mx,getmax(now<<1|1,mid+1,r,x,y));
            return sgt[now].delta+mx;
        }
    }
}t2;
int rt[maxn],fa[maxn],a[maxn],delta;
int n,m;
char op[5];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int main()
{
    n=read();
    t1.tot=0; delta=0;
    t1.sgt[0].mx=t2.sgt[0].mx=-inf;
    for(int i=1;i<=n;i++){
        a[i]=read();
        rt[i]=++t1.tot; fa[i]=i;
        t1.add(rt[i],1,n,i,i,a[i]);
        t2.add(1,1,n,i,i,a[i]);
    }
    m=read();
    for(int i=1;i<=m;i++){
        scanf("%s",op);
        if(op[0]=='U'){
            int x=read(),y=read();
            x=find(x); y=find(y);
            if(x!=y){
                fa[y]=x; t1.merge(rt[x],rt[y],0);
                t2.add(1,1,n,x,x,std::max(a[x],a[y])-a[x]); t2.add(1,1,n,y,y,-a[y]-inf);
                a[x]=std::max(a[x],a[y]); a[y]=-inf;
            }
        }
        else if(op[0]=='A'){
            if(op[1]=='1'){
                int x=read(),v=read(),fx=find(x);
                t1.add(rt[fx],1,n,x,x,v);
                int t=t1.getmax(rt[fx],1,n,1,n);
                t2.add(1,1,n,fx,fx,t-a[fx]);
                a[fx]=t;
            }
            else if(op[1]=='2'){
                int x=read(),v=read(),fx=find(x);
                t1.add(rt[fx],1,n,1,n,v);
                t2.add(1,1,n,fx,fx,v);
                a[fx]+=v;
            }
            else{
                int v=read();
                delta+=v;
            }
        }
        else{
            if(op[1]=='1'){
                int x=read(),fx=find(x);
                printf("%d
",t1.getmax(rt[fx],1,n,x,x)+delta);
            }
            else if(op[1]=='2'){
                int x=read(),fx=find(x);
                printf("%d
",a[fx]+delta);
            }
            else printf("%d
",t2.getmax(1,1,n,1,n)+delta);
        }
    }
}
bzoj2333 & luoguP3273

  可并堆的做法以后再补吧。。

原文地址:https://www.cnblogs.com/quzhizhou/p/10099529.html