BZOJ 2333 SCOI2011 棘手的操作 并查集+可并堆

。。题意概述就不写了,各位老爷如果是看着玩的可以去搜一下,如果是做题找来的也知道题干的。实际上是题干无法缩减懒得复制ORZ

首先处理一下集合的合并和单点值查询的问题。使用并查集,对于每个点记录标记d表示这个点的实际值比这个点加入并查集的时候的值w大了多少,对于每个点find的时候把这个点到代表元路径上的点的d(不包括代表元)的d加起来更新这个点的d,每一次查询某个点的当前值的时候就先find就可以直接用w+d+代表元的d(特判这个点是不是代表元)回答。特别注意为了保证正确性在merge的时候要把双方中的某一个点建立成另外一个新点,原来两个点的pa是这个新点。这样值的集合修改和查询就解决。

接下来是最大值的问题。这里用的是可并堆。开两个可并堆,一个维护每个集合(称为hp1),另一个维护每个集合中的最大值(称为hp2)。有点lazy的思想,因为单点修改只会影响这个点的值,所以说直接在hp1中调整这个点的位置(注意到可能是向下,也可能是向上),然后看此集合中最大值对应的元素编号是否改变。改变的话就在hp2中删掉原来的最大元素编号加入新的,否则如果修改的这个点就是其集合中的最大值元素就在hp2中调整位置;如果是集合修改的话思路同单点直接调整被修改集合中最大值在hp2中的位置;对于所有值修改的操作直接单独记录一个数输出的时候加上就可以了(不影响单调性)。

这样调整之后任意时刻可并堆中的所有元素的位置都是正确的,正确性得以保证(虽然这个自己yy出来的东西代码有点长?)

最后说一件事情,自己乱搞数据结构的时候一定注意。。。。。指针要改完改对。。。。。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<algorithm>
  6 #include<cmath>
  7 #include<queue>
  8 #include<set>
  9 #include<map>
 10 #include<vector>
 11 #include<cctype>
 12 using namespace std;
 13 const int MAXN=300005;
 14
 15 int N,Q,A[MAXN];
 16 struct union_find{
 17     static const int maxn=300005;
 18     int pa[maxn<<1],stk[maxn<<1],d[maxn<<1],id[maxn<<1],w[maxn<<1],stk_top,np,ADD;
 19     union_find(){ np=stk_top=ADD=0; };
 20     int newnode(int x) { w[++np]=x,pa[np]=np,d[np]=0; return np; }
 21     void initial(int n,int *a){
 22         for(int i=1;i<=n;i++) pa[i]=id[i]=i,w[i]=a[i],d[i]=0;
 23         np=n;
 24     }
 25     int find(int x)
 26     {
 27         while(pa[x]!=x) stk[++stk_top]=x,x=pa[x];
 28         int rt=x,add=0;
 29         while(stk_top) x=stk[stk_top],add+=d[x],d[x]=add,pa[x]=rt,stk_top--;
 30         return rt;
 31     }
 32     int val(int x) { find(x); return w[x]+d[x]+(pa[x]==x?0:d[pa[x]])+ADD; }
 33     bool judge(int x,int y) { return find(x)==find(y); }
 34     void merge(int x,int y) { pa[find(x)]=pa[find(y)]=newnode(val(y)-ADD); }
 35 }uf;
 36 struct mergeable_heap{
 37     static const int maxn=300005;
 38     int chd[maxn][2],fa[maxn];
 39     void initial(int n) { for(int i=1;i<=n;i++) chd[i][0]=chd[i][1]=fa[i]=0; }
 40     int val(int x) { return uf.val(uf.id[x]); }
 41     void link(int x,int d,int y) { chd[x][d]=y,fa[y]=x; }
 42     int root(int x) { while(fa[x]) x=fa[x]; return x; }
 43     int merge(int A,int B)
 44     {
 45         if(!A||!B) return A+B;
 46         if(val(A)<val(B)) swap(A,B);
 47         link(A,1,merge(chd[A][1],B)); swap(chd[A][0],chd[A][1]);
 48         return A;
 49     }
 50     void ins(int A,int B) { fa[A]=chd[A][0]=chd[A][1]=0; merge(A,B); }
 51     void del(int A)
 52     {
 53         if(A==root(A)) fa[merge(chd[A][0],chd[A][1])]=0;
 54         else{
 55             int d=A==chd[fa[A]][1];
 56             link(fa[A],d,merge(chd[A][0],chd[A][1]));
 57         }
 58     }
 59     int top(int x) { return val(root(x)); }
 60     void rot(int x)
 61     {
 62         int p=fa[x],e=x==chd[p][0];
 63         int a=chd[x][0],b=chd[x][1],c=chd[p][e],d=fa[p];
 64         link(p,0,a); link(p,1,b);
 65         link(x,0,e?p:c); link(x,1,e?c:p);
 66         link(d,chd[d][1]==p,x);
 67     }
 68     void adjust(int x)
 69     {
 70         while(fa[x]&&val(x)>val(fa[x])) rot(x);
 71         while(chd[x][0]||chd[x][1]){
 72             int y;
 73             if(!chd[x][0]||!chd[x][1]) y=chd[x][0]?chd[x][0]:chd[x][1];
 74             else y=val(chd[x][0])>val(chd[x][1])?chd[x][0]:chd[x][1];
 75             if(val(y)<=val(x)) break;
 76             rot(y);
 77         }
 78     }
 79 }hp1,hp2;
 80
 81 void _scanf(char &x)
 82 {
 83     x=getchar();
 84     while(x!=‘U‘&&x!=‘A‘&&x!=‘F‘) x=getchar();
 85 }
 86 void data_in()
 87 {
 88     scanf("%d",&N);
 89     for(int i=1;i<=N;i++) scanf("%d",&A[i]);
 90     scanf("%d",&Q);
 91 }
 92 void work()
 93 {
 94     uf.initial(N,A);
 95     hp1.initial(N); hp2.initial(N);
 96     for(int i=1;i<N;i++)
 97         hp2.merge(hp2.root(i),i+1);
 98     char op1; int op2,x,y,v,rx,ry;
 99     for(int i=1;i<=Q;i++){
100         _scanf(op1);
101         if(op1==‘U‘){
102             scanf("%d%d",&x,&y);
103             if(!uf.judge(uf.id[x],uf.id[y])){
104                 rx=hp1.root(x),ry=hp1.root(y);
105                 uf.merge(uf.id[x],uf.id[y]);
106                 hp1.merge(rx,ry);
107                 if(rx!=hp1.root(x)) hp2.del(rx); else hp2.del(ry);
108             }
109         }
110         else if(op1==‘A‘){
111             scanf("%d",&op2);
112             if(op2==1){
113                 scanf("%d%d",&x,&v);
114                 rx=hp1.root(x);
115                 uf.w[uf.id[x]]+=v; hp1.adjust(x);
116                 if(rx!=hp1.root(x)){
117                     int rt=max(hp2.fa[rx],max(hp2.chd[rx][0],hp2.chd[rx][1]));
118                     hp2.del(rx); rt=hp2.root(rt);
119                     hp2.ins(hp1.root(x),rt);
120                 }
121                 else if(rx==x) hp2.adjust(x);
122             }
123             else if(op2==2){
124                 scanf("%d%d",&x,&v);
125                 uf.d[uf.find(uf.id[x])]+=v;
126                 hp2.adjust(hp1.root(x));
127             }
128             else if(op2==3) scanf("%d",&v),uf.ADD+=v;
129         }
130         else if(op1==‘F‘){
131             scanf("%d",&op2);
132             if(op2==1) scanf("%d",&x),printf("%d\n",hp1.val(x));
133             else if(op2==2) scanf("%d",&x),printf("%d\n",hp1.top(x));
134             else if(op2==3) printf("%d\n",hp2.top(hp1.root(1)));
135         }
136     }
137 }
138 int main()
139 {
140     data_in();
141     work();
142     return 0;
143 }

原文地址:https://www.cnblogs.com/KKKorange/p/8468026.html

时间: 2024-10-11 11:10:56

BZOJ 2333 SCOI2011 棘手的操作 并查集+可并堆的相关文章

BZOJ 2333 SCOI2011 棘手的操作 可并堆套可并堆

题目大意:给定n个节点,每个节点有一个初始权值,维护以下操作: 1.合并两个联通块 2.某个点权值+x 3.某个点所在联通块权值+x 4.所有点权值+x 5.询问某个点的权值 6.询问某个点所在联通块的最大权值 7.询问所有点之间的最大权值 2333333333333333333333333333333333333333333333333333333333333 2333333333333333333333333333333333333333333333333333333333333 23333

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个节点所在的连通块的所有

【BZOJ】2333: [SCOI2011]棘手的操作

http://www.lydsy.com/JudgeOnline/problem.php?id=2333 #include <bits/stdc++.h> using namespace std; const int N=300015, Lim=N; struct node *null; struct node { node *c[2], *f; int s, tag, mx, w; void init(int _w=-(~0u>>2)) { c[0]=c[1]=f=null; s

bzoj2333[SCOI2011]棘手的操作

bzoj2333[SCOI2011]棘手的操作 题意: 有N个节点,M个操作:连接两个节点.单个节点的权值增加v.节点所在的连通块的所有节点的权值增加v.所有节点的权值增加v.询问节点当前的权值.询问节点所在的连通块中权值最大的节点的权值.询问所有节点中权值最大的节点的权值.N,M≤300000 题解: 可并堆,虽然听说配对堆非常快,但教程太少了不会写,所以去学了斜堆,比较好写.斜堆实际上是一棵二叉树,核心是合并操作,这是一个递归过程,有点像treap的删除操作.斜堆保证复杂度的方法是每次递归合

【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,代

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 2959 长跑(LCT+BCC+并查集)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2959 [题意] n个点,提供操作:连边,修改点权,查询自定义边的方向后起点a终点b能经过的最大点权和. [思路] 对于一个边的双连通分量,显然可以将权值全部获得. 如果没有连边操作,我们只需要将一个bcc缩点后求得a->b路径上的点权和即可. 加上连边后,使用并查集代表一个bcc,如果u,v之间不连通直接连边,如果已经连通则构成一个bcc,使用并查集将LCT的所有节点合并. 注意缩点

BZOJ 4025 二分图(时间树+并查集)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4025 [题目大意] 给出一张图,有些边只存在一段时间,问在一个每个时间段, 这张图是否是二分图 [题解] 判断是否是二分图只要判断是否存在奇环即可, 我们对时间进行分治,在操作树上加删边, 保留涵盖时间区间的有效操作,将剩余操作按时间划分到两端的子树, 退出子树的时候撤销加边操作. 对于判断奇环,我们用并查集维护每个点与标兵的相对距离的奇偶性即可, 由于需要撤销操作,我们放弃对并查集