【SCOI2011 Day2】棘手的操作

问题描述

 有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: 输出所有节点中,权值最大的节点的权值

输入格式

 输入的第一行是一个整数N,代表节点个数。
 接下来一行输入N个整数,a[1], a[2], …, a[N],代表N个节点的初始权值。
 再下一行输入一个整数Q,代表接下来的操作数。
 最后输入Q行,每行的格式如题目描述所示。

输出格式

 对于操作F1, F2, F3,输出对应的结果,每个结果占一行。

样例输入

3
0 0 0
8
A1 3 -20
A1 2 20
U 1 3
A2 1 10
F1 3
F2 3
A3 -10
F3

样例输出

-10
10
10

题解

棘手的操作果然很棘手,一来就有7个操作,给蒟蒻一个下马威。
首先看第一个操作——合并两个联通块,那么首选考虑LCT或可并堆这两个合并比较方便的数据结构。然而我们又发现还有点修改和联通块修改的操作,LCT打lazy很不方便(本人过弱),刚好又有查询联通块最大值,因此选择维护一个大根可并堆求解。这里选择了左偏树。
第一个操作:直接合并。
第二个操作(最棘手):讨论三种情况:当前点不与任何点联通,当点为树根,当前点为树的其他节点。为了维护堆的左偏性质,要把当前点从堆中删除后,调整原堆,再把修改后的点插入进去。(详见代码)
第三个操作:在堆顶打lazy标记,讨论该堆的点时下放,下方操作类似于LCT中Splay时的下放操作。
第四个操作:不影响点之间的相对大小,记一个全局变量,直接对其修改,每次输出一个值时加上该变量。
第五个操作:下放后输出。
第六个操作:直接查询。
第七个操作:开一个multiset,记录每一个联通块的最大值,合并和修改时要记得更新。

代码

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<set>
using namespace std;
const int maxn=3e5+5;
int a,b,n,q,add,temp,w[maxn],l[maxn],r[maxn],fa[maxn],dis[maxn],lazy[maxn];
char op[5];
multiset<int> s;
int getfa(int x)
{
    return (fa[x]==x)?x:getfa(fa[x]);
}
void Putdown(int x)
{
    if(l[x]) w[l[x]]+=lazy[x],lazy[l[x]]+=lazy[x];
    if(r[x]) w[r[x]]+=lazy[x],lazy[r[x]]+=lazy[x];
    lazy[x]=0;
}
int  merge(int x,int y)
{
    if(!x||!y) return x|y;
    if(lazy[x]) Putdown(x);
    if(lazy[y]) Putdown(y);
    if(w[x]<w[y]) swap(x,y);
    r[x]=merge(r[x],y),fa[r[x]]=x;
    if(dis[r[x]]>dis[l[x]]) swap(r[x],l[x]);
    dis[x]=dis[r[x]]+1;
    return x;
}
void downpath(int x)
{
    if(fa[x]!=x) downpath(fa[x]);
    if(lazy[x]) Putdown(x);
}
int main()
{
    scanf("%d",&n),dis[0]=-1;
    for(int i=1; i<=n; i++) scanf("%d",&w[i]),fa[i]=i,s.insert(w[i]);
    scanf("%d",&q);
    while(q--)
    {
        scanf("\n%s",op);
        if(op[0]=='U')
        {
            scanf("%d%d",&a,&b),a=getfa(a),b=getfa(b);
            if(a!=b) temp=a^b^merge(a,b),s.erase(s.find(w[temp]));
        }
        else if(op[0]=='A')
        {
            if(op[1]=='1')
            {
                scanf("%d%d",&a,&b),temp=getfa(a),downpath(a),s.erase(s.find(w[temp]));
                if(fa[a]==a)
                {
                    if(l[a]||r[a])
                    {
                        int ls=l[a],rs=r[a];
                        fa[ls]=ls,fa[rs]=rs;
                        l[a]=0,r[a]=0,w[a]+=b;
                        temp=merge(ls,rs),temp=merge(temp,a);
                    }
                    else w[a]+=b,temp=a;
                }
                else
                {
                    int ls=l[a],rs=r[a],f=fa[a];
                    fa[ls]=ls,fa[rs]=rs;
                    if(l[f]==a) l[f]=0;else r[f]=0;
                    l[a]=0,r[a]=0,w[a]+=b,fa[a]=a;
                    temp=merge(temp,merge(ls,rs)),temp=merge(temp,a);
                }
                s.insert(w[temp]);
            }
            else if(op[1]=='2')
            {
                scanf("%d%d",&a,&b),temp=getfa(a),s.erase(s.find(w[temp]));
                lazy[temp]+=b,w[temp]+=b,s.insert(w[temp]);
            }
            else scanf("%d",&a),add+=a;
        }
        else
        {
            if(op[1]=='1') scanf("%d",&a),downpath(a),printf("%d\n",w[a]+add);
            else if(op[1]=='2') scanf("%d",&a),a=getfa(a),printf("%d\n",w[a]+add);
            else printf("%d\n",*(--s.end())+add);
        }
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值