BZOJ2333: [SCOI2011]棘手的操作
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\n",v1[x]+sum(x)+all);} void F2(){int x;scanf("%d",&x);printf("%d\n",v1[find1(x)]+all);} void F3(){printf("%d\n",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; }