传送门

长得一点也不像线段树的线段树思维题。。。

看到连边首先想到了$LCT$。但是。。。$LCT$不能对一群不在同一条链上的节点修改和查询。

换个思路想:如果同一联通块节点编号连续的话,是不是就好维护了呢?这样问题就变成了保证每次连边后形成的联通块编号连续,然后就可以并查集$+$线段树维护了。

题目没有强制在线,自然可以把操作离线下来。

一开始每个点都是联通块,拥有一个编号序列(这里编号序列是一个相对位置,两个不同的联通块编号靠前靠后不会互相影响的)。当连接两个联通块时,把它们两个的编号序列拼接起来。蒟蒻用的朴素的链表,可以$O(1)$地将两个块编号序列连接起来。

然后用并查集维护联通块大小,线段树区间加维护最大值就好了$QwQ$。

注意!数据有不合法连边,要特判。

具体实现还是看代码清晰一些:(蒟蒻没写过链表,自己$yy$用数组写的的可能比较丑陋。。。)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>

#define maxn 300005
#define inf 0x3f3f3f3f

using namespace std;

inline int read(){
    int x=0,y=0;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return y?-x:x;
}
template<typename T>
inline T read(){
    T x=0;
    int y=0;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return y?-x:x;
}
int nex[maxn],tail[maxn],a[maxn],pos[maxn],seg[maxn],fa[maxn],siz[maxn],cnt,n;
//nex指向链表节点的下一项,tail是链表尾指针
//seg是重新编的号,pos是每个seg对应的实际位置
//fa是并查集
int find(int x){
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}
struct Order{
    int s,x,d;//s是操作类型
}o[maxn];//存储操作
struct Segment_Tree{
    int ma[maxn<<2],tag[maxn<<2];    
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
    inline void update(int node){
        ma[node]=max(ma[ls(node)],ma[rs(node)]);
    }
    inline void datadown(int node,int d){
        ma[node]+=d,tag[node]+=d;
    }
    inline void pushdown(int node){
        datadown(ls(node),tag[node]);
        datadown(rs(node),tag[node]);
        tag[node]=0;
    }
    void build(int l=1,int r=cnt,int node=1){
        if(l==r){
            ma[node]=a[pos[l]];
            fa[l]=l,siz[l]=1;
            //重置并查集和siz
            return;
        }
        int mid=l+r>>1;
        build(l,mid,ls(node));
        build(mid+1,r,rs(node));
        update(node);
    }
    void add(int L,int R,int l,int r,int node,int d){
        if(L<=l&&R>=r){
            datadown(node,d);
            return;
        }
        if(tag[node])pushdown(node);
        int mid=l+r>>1;
        if(L<=mid)add(L,R,l,mid,ls(node),d);
        if(R>mid)add(L,R,mid+1,r,rs(node),d);
        update(node);
    }
    int ask(int L,int R,int l,int r,int node){
        if(L<=l&&R>=r)return ma[node];
        if(tag[node])pushdown(node);
        int mid=l+r>>1,ans=-inf;
        if(L<=mid)ans=ask(L,R,l,mid,ls(node));
        if(R>mid)ans=max(ans,ask(L,R,mid+1,r,rs(node)));
        return ans;
    }
}st;//线段树常规操作
int main(){
    n=read();
    for(register int i=1;i<=n;++i)
        a[i]=read(),tail[i]=fa[i]=i;
    int m=read();
    for(register int i=1;i<=m;++i){
        char s[5];
        scanf("%s",s);
        if(s[0]=='A'){
            o[i].s=-s[1]+'0';
            if(s[1]<'3')o[i].x=read();
            o[i].d=read();
        }
        else if(s[0]=='F'){
            o[i].s=s[1]-'0';
            if(s[1]<'3')o[i].x=read();
        }
        else {
            o[i].s=45562;
            int x=read(),y=read(),u=find(x),v=find(y);
            o[i].x=x,o[i].d=y;
            if(u==v)continue;//特判
            nex[tail[u]]=v,tail[u]=tail[v],fa[v]=u;
            //把两段链表连起来
        }
    }
    //s<0为A操作,|s|为其后跟的编号
    //s==45562连边操作
    //s>0为F操作,s为其后跟的编号
    for(register int i=1;i<=n;++i)
        if(fa[i]==i){
            //如果一个点在并查集里是根的话,它也是所在链表的头
            for(register int j=i;j;j=nex[j])seg[j]=++cnt,pos[cnt]=j;
            //遍历每一个链表,直接按链表顺序编号
        }
    st.build();//初始建树
    for(register int i=1;i<=m;++i){
        int s=o[i].s;
        if(s==45562){
            int u=find(o[i].x),v=find(o[i].d);
            if(u==v)continue;
            siz[u]+=siz[v],fa[v]=u;
            //维护一下siz便于获取联通块的区间
        }
        else if(s<0){
            if(s==-1)st.add(seg[o[i].x],seg[o[i].x],1,cnt,1,o[i].d);
            else if(s==-2){
                int u=find(o[i].x);
                st.add(seg[u],seg[u]+siz[u]-1,1,cnt,1,o[i].d);
                //并查集根的编号+siz-1就是该联通块当前所代表的区间
            }
            else st.add(1,cnt,1,cnt,1,o[i].d);
        }
        else {
            if(s==1)printf("%d\n",st.ask(seg[o[i].x],seg[o[i].x],1,cnt,1));
            else if(s==2){
                int u=find(o[i].x);
                printf("%d\n",st.ask(seg[u],seg[u]+siz[u]-1,1,cnt,1));
            }
            else printf("%d\n",st.ma[1]);
        }
    }
}