2333: [SCOI2011]棘手的操作
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2537 Solved: 985
[Submit][Status][Discuss]
Description
有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: 输出所有节点中,权值最大的节点的权值
Input
输入的第一行是一个整数N,代表节点个数。
接下来一行输入N个整数,a[1], a[2], …, a[N],代表N个节点的初始权值。
再下一行输入一个整数Q,代表接下来的操作数。
最后输入Q行,每行的格式如题目描述所示。
Output
对于操作F1, F2, F3,输出对应的结果,每个结果占一行。
Sample Input
0 0 0
8
A1 3 -20
A1 2 20
U 1 3
A2 1 10
F1 3
F2 3
A3 -10
F3
Sample Output
10
10
HINT
对于30%的数据,保证 N<=100,Q<=10000
对于80%的数据,保证 N<=100000,Q<=100000
对于100%的数据,保证 N<=300000,Q<=300000
对于所有的数据,保证输入合法,并且 -1000<=v, a[1], a[2], …, a[N]<=1000
2.SCOI2011棘手的操作
完成时间:2017.7.23.21.35
时耗:3h
思路{
题号有点小牛逼。。。。。。。。。
看到合并,查询最大值,我们果断想到了可并堆。
操作1:合并两个左偏树,找出两个节点所属的左偏树,合并即可
操作2:给一特定节点的值增加v,我们可以先取出这个元素,修改它的值,合并。
操作3:给一个左偏树的所有值增加一个数,像线段树那样打个标记都可以了。
操作4;直接搞个全局变量记录all都可以辣。
查询1:输出x节点的权值=当前的值加上跳父亲的lazy值+全局all。
查询2:直接取堆顶元素即可。
查询3:这个只要取各个堆的堆顶元素建一个堆,在各种操作中先删除,再单点修改,再插入。
}
//第一次写可并堆的数据结构火题,还是写下注释吧。。。。。。
#include<bits/stdc++.h>
#define RG register
#define il inline
#define N 300010
using namespace std;
int l1[N],r1[N],d1[N],d2[N],v1[N],lazy[N],l2[N],r2[N],v2[N],f1[N],f2[N],rt,n,q,all;
void down(int x){
v1[l1[x]]+=lazy[x],v1[r1[x]]+=lazy[x];
lazy[l1[x]]+=lazy[x],lazy[r1[x]]+=lazy[x];
lazy[x]=0;
}//下放懒标记。。
int find1(int x){int X=x;while(f1[X])X=f1[X];return X;}//找霸霸
int sum(int x){int sum=0,X=f1[x];while(X)sum+=lazy[X],X=f1[X];return sum;}//累加LAZY
int merge1(int x,int y){//大根堆
if(!x||!y)return x+y;
if(v1[x]<v1[y])swap(x,y);
down(x);r1[x]=merge1(r1[x],y);f1[r1[x]]=x;
if(d1[r1[x]]>d1[l1[x]])swap(r1[x],l1[x]);
d1[x]=d1[r1[x]]+1;return x;
}
int merge2(int x,int y){
if(!x||!y)return x+y;
if(v2[x]<v2[y])swap(x,y);
r2[x]=merge2(r2[x],y);f2[r2[x]]=x;
if(d2[r2[x]]>d2[l2[x]])swap(r2[x],l2[x]);
d2[x]=d2[r2[x]]+1;return x;
}
int del1(int x){down(x);
int le=l1[x],ri=r1[x];int y=merge1(le,ri);
if(x==l1[f1[x]])l1[f1[x]]=y;else r1[f1[x]]=y;
f1[y]=f1[x];return find1(y);
}
void del2(int x){
int le=l2[x],ri=r2[x];int y=merge2(le,ri);
if(rt==x)rt=y;
if(x==l2[f2[x]])l2[f2[x]]=y;else r2[f2[x]]=y;
f2[y]=f2[x];
}
void getnode1(int x,int v){f1[x]=d1[x]=l1[x]=r1[x]=0,v1[x]=v;}
void getnode2(int x,int v){f2[x]=d2[x]=l2[x]=r2[x]=0,v2[x]=v;}
void U(){
int x,y;scanf("%d%d",&x,&y);
x=find1(x),y=find1(y);
if(x!=y){
if(merge1(x,y)==x)del2(y);else del2(x);
}
}
void A1(){
int x,v;scanf("%d%d",&x,&v);
del2(find1(x));//堆顶元素可能不是最优的了。。。。。
int y=del1(x);
getnode1(x,v+v1[x]+sum(x));
int z=merge1(y,x);getnode2(z,v1[z]);
rt=merge2(rt,z);
}
void A2(){
int x,v;scanf("%d%d",&x,&v);
x=find1(x);v1[x]+=v;lazy[x]+=v;
del2(x);getnode2(x,v1[x]);rt=merge2(rt,x);
}
void A3(){int v;scanf("%d",&v);all+=v;}
void F1(){int x;scanf("%d",&x);printf("%d
",v1[x]+sum(x)+all);}
void F2(){int x;scanf("%d",&x);printf("%d
",v1[find1(x)]+all);}
void F3(){printf("%d
",v2[rt]+all);}
int main(){
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&v1[i]),v2[i]=v1[i];
scanf("%d",&q);rt=1;for(int i=2;i<=n;++i)rt=merge2(rt,i);char ch[3];
for(int i=1;i<=q;++i){
scanf("%s",ch);
if(ch[0]=='U')U();
if(ch[0]=='A'){
if(ch[1]=='1')A1();
if(ch[1]=='2')A2();
if(ch[1]=='3')A3();
}
if(ch[0]=='F'){
if(ch[1]=='1')F1();
if(ch[1]=='2')F2();
if(ch[1]=='3')F3();
}
}return 0;
}