[SCOI2011]棘手的操作

题目传送-Luogu3273

题目传送-BZOJ2333

题意:

\(n\)个节点,标号从\(1\)\(n\),这\(n\)个节点一开始相互不连通。第\(i\)个节点的初始权值为\(a_i\),接下来有如下一些操作:
\(U\) \(x\) \(y\): 加一条边,连接第\(x\)个节点和第\(y\)个节点
\(A1\) \(x\) \(v\): 将第\(x\)个节点的权值增加\(v\)
\(A2\) \(x\) \(v\): 将第\(x\)个节点所在的连通块的所有节点的权值都增加\(v\)
\(A3\) \(v\): 将所有节点的权值都增加\(v\)
\(F1\) \(x\): 输出第\(x\)个节点当前的权值
\(F2\) \(x\): 输出第\(x\)个节点所在的连通块中,权值最大的节点的权值
\(F3\): 输出所有节点中,权值最大的节点的权值

题解:

一片文章
有合并操作,还有在点上的权值,并查集显然不能用,考虑可并堆。
U:两个可并堆并起来
A1:这个点从左偏树里删掉,加上权值后再并起来,注意标记的处理
A2:给根打上标记
A3:记个全局变量加一下
F1:标记下传后输出
F2:输出这个堆顶值
F3:全局维护个set维护所有堆顶的值,记得更新

过程:

一堆细节真是恶心。。hzwer:嘴巴AC真是简单

代码:

const int N=300010;
int n,q,Add=0; set<int> Max; map<int,int> cnt;
#define lc ch[u][0]
#define rc ch[u][1]
int hp[N],ch[N][2],laz[N],fa[N],dep[N]; 
inline void Erase(int val) {if(--cnt[val]==0) Max.erase(val);}
inline void Insert(int val) {if(++cnt[val]==1) Max.insert(val);}
inline bool get_son(int u) {return u==ch[fa[u]][1];}
inline int Find(int x) {
    // assert(x);
    while(fa[x]) x=fa[x]; return x;
}
inline void Update(int u,int v) {
    assert(u);
    hp[u]+=v; laz[u]+=v;
}
inline void Push_Dn(int u) {
    if(laz[u]) {
        int val=laz[u]; laz[u]=0;
        if(lc) Update(lc,val);
        if(rc) Update(rc,val);
    }
}
int que[N];
inline void Pulltag(int u) {
    int tot=0;
    while(u)que[++tot]=u,u=fa[u]; 
    while(tot) Push_Dn(que[tot--]);
}
int Merge(int x,int y) {
    if(!x || !y) return x+y;
    // cerr<<x<<' '<<y<<endl;
    if(hp[x]<hp[y]) swap(x,y);
    Push_Dn(x);
    ch[x][1]=Merge(ch[x][1],y);
    assert(ch[x][1]);
    fa[ch[x][1]]=x;
    if(dep[ch[x][0]]<dep[ch[x][1]]) swap(ch[x][0],ch[x][1]);
    dep[x]=dep[ch[x][1]]+1;
    return x;
}
inline void Solve_A1(int u,int v) {
    int anc=Find(u);  Erase(hp[anc]);
    Pulltag(u);
    // cerr<<"Arr"<<endl;
    fa[lc]=fa[rc]=0; 
    int top=Merge(lc,rc),f=fa[u];
    ch[f][get_son(u)]=top;
    lc=rc=fa[u]=0;
    fa[top]=f;
    hp[u]+=v;
    anc=Find(top);
    // printf("%d %d\n",anc,top);
    anc=Merge(anc,u);
    // printf("%d\n",anc);
    Insert(hp[anc]);
}
char s[100];
inline void Assert() {
    // assert(fa[0]==0); assert(ch[0][0]==0); assert(ch[0][1]==0);
    for(int i=1;i<=n;i++)
        for(int j=0;j<2;j++) {
            if(ch[i][j]) {
                // if(fa[ch[i][j]]!=i) {
                //  cerr<<fa[ch[i][j]]<<' '<<ch[i][j]<<' '<<i<<endl;
                // }
                assert(fa[ch[i][j]]==i);
                assert(fa[i]!=i);
                assert(i!=ch[i][j]);
            }
        }
}
signed main() {
    // freopen("kittle4.in","r",stdin);
    // freopen("my.out","w",stdout);
    set<int> :: iterator it;
    read(n);
    for(int i=1;i<=n;i++) read(hp[i]),Insert(hp[i]);
    read(q); int Cnt=0;
    for(int i=1;i<=q;i++) {
        //Assert();
        // cerr<<"i="<<i<<endl;
        scanf("%s",s+1); int x,y,v;
        if(s[1]=='U') {
            read(x); read(y);
            int fx=Find(x),fy=Find(y);
            if(fx==fy) continue;
            Erase(hp[fx]); Erase(hp[fy]);
            x=Merge(fx,fy);
            Insert(hp[x]);
        } else if(s[1]=='F') {
            ++Cnt; //if(Cnt==117) cerr<<s[2];
            if(s[2]=='1') {
                read(x); Pulltag(x);
                printf("%d\n",hp[x]+Add);
            } else if(s[2]=='2') {
                read(x);
                printf("%d\n",hp[Find(x)]+Add);
            } else if(s[2]=='3') {
                it=Max.end(); it--;
                printf("%d\n",*it+Add);
            }
        } else if(s[1]=='A') {
            if(s[2]=='1') {
                read(x); read(v);
                Solve_A1(x,v);
            } else if(s[2]=='2') {
                read(x); read(v); int anc=Find(x);
                Erase(hp[anc]);
                Update(anc,v);
                Insert(hp[anc]);
            } else if(s[2]=='3') {
                read(v); Add+=v;
            }
        }

    }
    return 0;
}
/*
*/

用时:1h

转载于:https://www.cnblogs.com/functionendless/p/9470836.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值