问题描述
有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;
}