2016-05-31 21:45:41
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2333
(学习了黄学长的代码
有如下操作:
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就是一个合并操作,用可并堆,注意tag的下传
A1将x点从所在堆中删去,修改权值后再加进去。删除就是合并两棵子树,在将merge后节点的父亲改为x的父亲,返回find(merge后的节点),因为x有可能是根
A2的话在所在堆的堆顶上加tag
A3再开一个变量记录好了
F1 记得将祖先的tag标记pushdown
F2 堆顶+A3
F3 比较复杂,网上很多做法都是在来一棵左偏树,维护各个堆的堆顶。在这里学习了黄学长,用multiset来维护,注意要实时更新里面的信息。
1 #include<bits/stdc++.h> 2 #define inf 1000000000 3 #define ll long long 4 #define N 300005 5 using namespace std; 6 int read(){ 7 int x=0,f=1;char ch=getchar(); 8 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 9 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 10 return x*f; 11 } 12 int n,Q,overadd,fa[N],tag[N],ls[N],rs[N],v[N],dep[N]; 13 multiset<int> st; 14 char ch[5]; 15 int find(int x){ 16 while(fa[x])x=fa[x]; 17 return x; 18 } 19 void pushdown(int x){ 20 if(!tag[x])return; 21 if(ls[x])tag[ls[x]]+=tag[x],v[ls[x]]+=tag[x]; 22 if(rs[x])tag[rs[x]]+=tag[x],v[rs[x]]+=tag[x]; 23 tag[x]=0; 24 } 25 int merge(int x,int y){ 26 if(!x||!y)return x+y; 27 if(v[x]<v[y])swap(x,y); 28 pushdown(x); 29 rs[x]=merge(rs[x],y); 30 fa[rs[x]]=x; 31 if(dep[ls[x]]<dep[rs[x]])swap(ls[x],rs[x]); 32 dep[x]=dep[rs[x]]+1; 33 return x; 34 } 35 void unite(int x,int y){ 36 int fx=find(x),fy=find(y); 37 if(fx!=fy){ 38 int t=merge(fx,fy); 39 if(t==fx)st.erase(st.find(v[fy])); 40 else st.erase(st.find(v[fx])); 41 } 42 } 43 void Pushdown(int x){ 44 if(fa[x])Pushdown(fa[x]); 45 pushdown(x); 46 } 47 int del(int x){ 48 int t=merge(ls[x],rs[x]),f=fa[x]; 49 ls[x]=rs[x]=fa[x]=0; 50 if(x==ls[f])ls[f]=t; 51 else rs[f]=t; 52 fa[t]=f; 53 return find(t); 54 } 55 void add(int x,int val){ 56 Pushdown(x); 57 st.erase(st.find(v[find(x)])); 58 v[x]+=val; 59 st.insert(v[merge(x,del(x))]); 60 } 61 void change(int x,int val){ 62 int f=find(x); 63 tag[f]+=val;v[f]+=val; 64 st.erase(st.find(v[f]-val));st.insert(v[f]); 65 } 66 void getval(int x){ 67 Pushdown(x); 68 printf("%d\n",v[x]+overadd); 69 } 70 int main(){ 71 n=read(); 72 for(int i=1;i<=n;i++)v[i]=read(),st.insert(v[i]); 73 Q=read(); 74 while(Q--){ 75 scanf("%s",ch); 76 if(ch[0]=='A'){ 77 if(ch[1]=='1'){ 78 int x=read(),y=read();add(x,y); 79 } 80 else if(ch[1]=='2'){ 81 int x=read(),y=read();change(x,y); 82 } 83 else overadd+=read(); 84 } 85 else if(ch[0]=='F'){ 86 if(ch[1]=='1')getval(read()); 87 else if(ch[1]=='2')getval(find(read())); 88 else printf("%d\n",*--st.find(inf)+overadd); 89 } 90 else{ 91 int x=read(),y=read();unite(x,y); 92 } 93 } 94 return 0; 95 }
2333: [SCOI2011]棘手的操作
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1621 Solved: 620
[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
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