bzoj2333[SCOI2011]棘手的操作

bzoj2333[SCOI2011]棘手的操作

题意:

有N个节点,M个操作:连接两个节点、单个节点的权值增加v、节点所在的连通块的所有节点的权值增加v、所有节点的权值增加v、询问节点当前的权值、询问节点所在的连通块中权值最大的节点的权值、询问所有节点中权值最大的节点的权值。N,M≤300000

题解:

可并堆,虽然听说配对堆非常快,但教程太少了不会写,所以去学了斜堆,比较好写。斜堆实际上是一棵二叉树,核心是合并操作,这是一个递归过程,有点像treap的删除操作。斜堆保证复杂度的方法是每次递归合并右节点,合并完后交换左右节点,使整棵树和splay一样,可以“自动”平衡,也是玄学。要修改整个连通块,打标记就行了。这道题特殊的一点在于询问所有节点权值的最大值,可以用STL的set维护所有连通块的根节点,当连边和修改权值时如果根节点被修改需要维护一下set。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <set>
 5 #define inc(i,j,k) for(int i=j;i<=k;i++)
 6 #define maxn 300100
 7 #define INF 0x3fffffff
 8 using namespace std;
 9
10 int fa[maxn],ch[maxn][2],tg[maxn],v[maxn],n,m,add;
11 multiset <int> st;
12 void pushdown(int x){
13     if(tg[x]){
14         if(ch[x][0])tg[ch[x][0]]+=tg[x],v[ch[x][0]]+=tg[x];
15         if(ch[x][1])tg[ch[x][1]]+=tg[x],v[ch[x][1]]+=tg[x];
16         tg[x]=0;
17     }
18 }
19 int dt[maxn],dts;
20 int find(int x){
21     dt[dts=1]=x; while(fa[x])x=fa[x],dt[++dts]=x;
22     for(int i=dts;i>=1;i--)pushdown(dt[i]); return x;
23 }
24 int merge(int x,int y){
25     if(!x||!y)return x+y; if(v[x]<v[y])swap(x,y); pushdown(x);
26     ch[x][1]=merge(ch[x][1],y); fa[ch[x][1]]=x; swap(ch[x][0],ch[x][1]); return x;
27 }
28 int del(int x){
29     int t=merge(ch[x][0],ch[x][1]),f=fa[x]; fa[x]=ch[x][0]=ch[x][1]=0;
30     fa[t]=f; if(f)ch[f][ch[f][1]==x]=t; return t;
31 }
32 void update1(int x,int val){
33     int y=find(x); int t=del(x); v[x]+=val;
34     if(y!=x){
35         int z=merge(y,x); st.erase(st.find(v[y])); st.insert(v[z]);
36     }else{
37         if(t){
38             int z=merge(t,x); st.erase(st.find(v[x]-val)),st.insert(v[z]);
39         }else st.erase(st.find(v[x]-val)),st.insert(v[x]);
40     }
41 }
42 void update2(int x,int val){
43     x=find(x); tg[x]+=val; v[x]+=val; if(!fa[x])st.erase(st.find(v[x]-val)),st.insert(v[x]);
44 }
45 void update3(int val){add+=val;}
46 int query1(int x){find(x); return v[x];}
47 int query2(int x){int y=find(x); return v[y];}
48 int query3(){return * --st.find(INF);}
49 void connect(int x,int y){
50     int xx=find(x),yy=find(y); if(xx==yy)return; int z=merge(xx,yy);
51     if(z==xx)st.erase(st.find(v[yy]));else st.erase(st.find(v[xx]));
52 }
53 char opt[3];
54 int main(){
55     //freopen("test.txt","r",stdin);
56     scanf("%d",&n); add=0; st.clear();
57     inc(i,1,n){
58         scanf("%d",&v[i]); st.insert(v[i]); fa[i]=ch[i][0]=ch[i][1]=tg[i]=0;
59     }
60     scanf("%d",&m);
61     inc(i,1,m){
62         scanf("%s",opt); int x,y;
63         if(opt[0]==‘U‘)scanf("%d%d",&x,&y),connect(x,y);
64         if(opt[0]==‘A‘){
65             if(opt[1]==‘1‘)scanf("%d%d",&x,&y),update1(x,y);
66             if(opt[1]==‘2‘)scanf("%d%d",&x,&y),update2(x,y);
67             if(opt[1]==‘3‘)scanf("%d",&x),update3(x);
68         }
69         if(opt[0]==‘F‘){
70             if(opt[1]==‘1‘)scanf("%d",&x),printf("%d\n",query1(x)+add);
71             if(opt[1]==‘2‘)scanf("%d",&x),printf("%d\n",query2(x)+add);
72             if(opt[1]==‘3‘)printf("%d\n",query3()+add);
73         }
74         //if(i==2)break;
75     }
76     return 0;
77 }

20160530

时间: 2024-12-14 09:09:44

bzoj2333[SCOI2011]棘手的操作的相关文章

bzoj2333 [SCOI2011]棘手的操作(洛谷3273)

题目描述 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作:U x y: 加一条边,连接第x个节点和第y个节点A1 x v: 将第x个节点的权值增加vA2 x v: 将第x个节点所在的连通块的所有节点的权值都增加vA3 v: 将所有节点的权值都增加vF1 x: 输出第x个节点当前的权值F2 x: 输出第x个节点所在的连通块中,权值最大的节点的权值F3: 输出所有节点中,权值最大的节点的权值 输入输出格式 输入格式: 输入的第一行是一个整数

bzoj千题计划217:bzoj2333: [SCOI2011]棘手的操作

http://www.lydsy.com/JudgeOnline/problem.php?id=2333 读入所有数据,先模拟一遍所有的合并操作 我们不关心联通块长什么样,只关心联通块内有谁 所以可以把一个联通块用一个链表存储 合并x和y时,y的链表整体接到x的链表后面 这样就成了线性结构 按照链表顺序重新给序列标号即可用线段树维护 一遍过,^_^ #include<cstdio> #include<iostream> #include<algorithm> using

bzoj千题计划218:bzoj2333: [SCOI2011]棘手的操作

http://www.lydsy.com/JudgeOnline/problem.php?id=2333 上次那个是线段树,再发一个左偏树 维护两种左偏树 第一种是对每个联通块维护一个左偏树 第二种是对所有第一种左偏树的根节点维护一个左偏树 #include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define N 300001 /*void read(int &x

bzoj2333[SCOI2011]棘手的操作 洛谷P3273 [SCOI2011]棘手的操作

2333? 先记一下吧,这题现在全部都是照着题解做的,因为怎么改都改不出来,只好对着题解改,以后还要再做过 以后再也不用指针了!太恶心了!空指针可不止直接特判那么简单啊,竟然还要因为空指针写奇怪的分类讨论! 没错,就是那个诡异的55和63行.由于要返回删除x后x所在树的新根,要分类讨论:如果x是根且其两个子节点合并后为空,那么去掉x后新树树根为空:如果x是根且其两个子节点合并后不为空,那么去掉x后新树树根为两个子节点合并后的:如果x不是根,那么去掉x后新树树根为原来的find(x). 另外,打了

2333: [SCOI2011]棘手的操作[离线线段树]

2333: [SCOI2011]棘手的操作 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2325  Solved: 909[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个节点所在的连通块的所有

2333: [SCOI2011]棘手的操作[写不出来]

2333: [SCOI2011]棘手的操作 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1979  Solved: 772[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个节点所在的连通块的所有

【BZOJ2333】棘手的操作(左偏树,STL)

[BZOJ2333]棘手的操作(左偏树,STL) 题面 BZOJ上看把... 题解 正如这题的题号 我只能\(2333\) 神TM棘手的题目... 前面的单点/联通块操作 很显然是一个左偏树+标记 (确实很显然,只是写死人...) 然后对于全局的最大值而言 搞一个\(multi\)来水 看起来真的简单.. 写起来真的想死... 记住:要特判一下已经联通的块就不要再去\(Merge\)了 #include<iostream> #include<cstdio> #include<

【bzoj2333】 SCOI2011—棘手的操作

http://www.lydsy.com/JudgeOnline/problem.php?id=2333 (题目链接) 题意 N个节点维护一些操作.. Solution 我们用可并大根堆进行维护. 对于每个连通块建一个局部可并堆,因为要询问全局最大值,所以还要对全局建一个全局可并堆记录之前局部可并堆堆顶元素. U:合并x所在的堆以及y所在的堆,并在全局堆中删除合并前的局部堆堆顶元素,因为它合并以后已经不是其连通块的堆顶了. A1:在堆中删除,更新后再加入堆 A2:找到其堆顶,对堆顶进行修改并打上

【bzoj2333】[SCOI2011]棘手的操作 可并堆+STL-set

题目描述 有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,代