bzoj 2959 长跑(LCT+BCC+并查集)

【题目链接】

http://www.lydsy.com/JudgeOnline/problem.php?id=2959

【题意】

n个点,提供操作:连边,修改点权,查询自定义边的方向后起点a终点b能经过的最大点权和。

【思路】

对于一个边的双连通分量,显然可以将权值全部获得。

如果没有连边操作,我们只需要将一个bcc缩点后求得a->b路径上的点权和即可。

加上连边后,使用并查集代表一个bcc,如果u,v之间不连通直接连边,如果已经连通则构成一个bcc,使用并查集将LCT的所有节点合并。

注意缩点后以并查集的代表元为该点,Access上找父亲的时候应该用父亲的bcc代表元。

【代码】

  1 #include<set>
  2 #include<cmath>
  3 #include<queue>
  4 #include<vector>
  5 #include<cstdio>
  6 #include<cstring>
  7 #include<iostream>
  8 #include<algorithm>
  9 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
 10 using namespace std;
 11
 12 typedef long long ll;
 13 const int N = 3e5+10;
 14
 15 ll read() {
 16     char c=getchar();
 17     ll f=1,x=0;
 18     while(!isdigit(c)) {
 19         if(c==‘-‘) f=-1; c=getchar();
 20     }
 21     while(isdigit(c))
 22         x=x*10+c-‘0‘,c=getchar();
 23     return x*f;
 24 }
 25
 26 struct UFS {
 27     int p[N];
 28     int ifind(int x) {
 29         if(p[x]==0||x==p[x]) return p[x]=x;
 30         return p[x]=ifind(p[x]);
 31     }
 32     void iunion(int x,int y) {
 33         x=ifind(x),y=ifind(y);
 34         if(x!=y) p[x]=y;
 35     }
 36 } bcc,S;
 37
 38 namespace LCT {
 39
 40     struct Node {
 41         Node *ch[2],*fa;
 42         int v,rev,sum;
 43         Node() {}
 44         Node(int x) ;
 45         void reverse() {
 46             rev^=1;
 47             swap(ch[0],ch[1]);
 48         }
 49         void up_push() {
 50             if(fa->ch[0]==this||fa->ch[1]==this)
 51                 fa->up_push();
 52             if(rev) {
 53                 ch[0]->reverse();
 54                 ch[1]->reverse();
 55                 rev=0;
 56             }
 57         }
 58         void maintain() {
 59             sum=v+ch[0]->sum+ch[1]->sum;
 60         }
 61     } T[N],*null=&T[0];
 62     Node::Node(int x) {
 63         rev=0;
 64         v=sum=x;
 65         ch[0]=ch[1]=fa=null;
 66     }
 67
 68     void rot(Node* o,int d) {
 69         Node *p=o->fa;
 70         p->ch[d]=o->ch[d^1];
 71         o->ch[d^1]->fa=p;
 72         o->ch[d^1]=p;
 73         o->fa=p->fa;
 74         if(p==p->fa->ch[0])
 75             p->fa->ch[0]=o;
 76         else if(p==p->fa->ch[1])
 77             p->fa->ch[1]=o;
 78         p->fa=o;
 79         p->maintain();
 80     }
 81     void splay(Node *o) {
 82         o->up_push();
 83         Node *nf,*nff;
 84         while(o->fa->ch[0]==o||o->fa->ch[1]==o) {
 85             nf=o->fa,nff=nf->fa;
 86             if(o==nf->ch[0]) {
 87                 if(nf==nff->ch[0]) rot(nf,0);
 88                 rot(o,0);
 89             } else {
 90                 if(nf==nff->ch[1]) rot(nf,1);
 91                 rot(o,1);
 92             }
 93         }
 94         o->maintain();
 95     }
 96     void Access(Node *o) {
 97         Node *son=null;
 98         while(o!=null) {
 99             splay(o);
100             o->ch[1]=son;
101             o->maintain();
102             o->fa=&T[bcc.ifind(o->fa-T)];
103             son=o; o=o->fa;
104         }
105     }
106     void evert(Node *o) {
107         Access(o);
108         splay(o);
109         o->reverse();
110     }
111     void Link(Node *u, Node *v) {
112         evert(u);
113         u->fa=v;
114     }
115
116 }
117
118 using namespace LCT;
119
120 int n,m,a[N];
121
122 void merge(Node* &u,Node* v)
123 {
124     if(u==null) return ;
125     v->v+=u->v;
126     bcc.iunion(u-T,v-T);
127     merge(u->ch[0],v);
128     merge(u->ch[1],v);
129     u=null;
130 }
131
132 int main()
133 {
134     n=read(),m=read();
135     FOR(i,1,n) {
136         a[i]=read(); T[i]=Node(a[i]);
137     }
138     int op,u,v;
139     FOR(i,1,m) {
140         op=read(),u=read(),v=read();
141         if(op==1) {
142             u=bcc.ifind(u),v=bcc.ifind(v);
143             if(u==v) continue;
144             if(S.ifind(u)!=S.ifind(v))
145                 Link(&T[u],&T[v]),
146                 S.iunion(u,v);
147             else {
148                 evert(&T[u]);
149                 Access(&T[v]),splay(&T[v]);
150                 merge(T[v].ch[0],&T[v]);
151                 merge(T[v].ch[1],&T[v]);
152                 T[v].maintain();
153             }
154         } else
155         if(op==2) {
156             splay(&T[bcc.ifind(u)]);
157             T[bcc.ifind(u)].v+=v-a[u];
158             T[bcc.ifind(u)].maintain();
159             a[u]=v;
160         } else {
161             u=bcc.ifind(u),v=bcc.ifind(v);
162             if(S.ifind(u)!=S.ifind(v)) puts("-1");
163             else {
164                 evert(&T[u]);
165                 Access(&T[v]),splay(&T[v]);
166                 printf("%d\n",T[v].sum);
167             }
168         }
169     }
170     return 0;
171 }

Q:打码的最怕什么

A:手残和眼瞎。

真不巧,我两样都是

时间: 2024-08-03 04:04:25

bzoj 2959 长跑(LCT+BCC+并查集)的相关文章

bzoj 2959: 长跑

bzoj 2959: 长跑 可以看作是这道题的加强版:https://www.luogu.org/problemnew/show/P2542 看到动态维护\(Tarjan\)缩点就考虑使用\(LCT\),使用一个并查集\(f\)表示点\(i\)所在的点双的"标志"点是哪一个,\(link\)时分类讨论一下即可 然后这题卡常,我们无法使用\(findroot\),于是考虑再用一个并查集表示两点之间的连通性即可 剩下的就和那个弱化版差不多了 #include<iostream>

BZOJ 3562: [SHOI2014]神奇化合物 并查集+dfs

点击打开链接 注意到20w条边,但是询问只有1w,所以有很多边是从头到尾不变的. 首先离线处理,将从未删除的边缩点,缩点后的图的点数不会超过2w,对于每一次add或者delete,直接dfs看是否能从a走到b,然后维护一个ans. 数据不强,不然这种复杂度起码要跑10s.. #include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> using namespace st

BZOJ 2959 长跑 Link-Cut-Tree+并查集

题目大意:给定n个点,支持以下操作: 1.在某两个点之间连接一条无向边 2.改变某个点的权值 3.将每条边设定一个方向,然后从x走到y,求能经过的所有点的权值和 首先如果这个图是静态的,我们把边双都缩点,那么每次询问显然就是两个点所在边双路径上的点权和 现在图是动态的,因此我们用动态树维护一下就行了 如果连边的两个点不连通,就在LCT中连接这两个点 如果连边的两个点已经连通,就将这个两个点路径上的所有点用并查集缩点 时间复杂度O(mlogn) 似乎用链剖会快一些? #include <cstdi

bzoj 2733 永无乡 - 并查集 - 线段树

永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的.现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥.Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输

bzoj 2936 [Poi 1999] 降水 - 并查集

题目传送门 需要root权限的传送门 题目大意 有一个$n\times m$的网格图,每一格都有一个高度.一次降雨过后问最多能积多少水. 考虑算每一高度能储存的水的量. 如果小于等于这个高度的格子和边界连通,那么水就会流走,这一部分不能算入答案. 所以用并查集维护高度小于等于当前高度的格子的连通性.每次答案加已经找到的格子数目减去和边界连通的格子数. Code 1 /** 2 * bzoj 3 * Problem#2936 4 * Accepted 5 * Time: 56ms 6 * Memo

【BZOJ 3674】可持久化并查集加强版&amp;【BZOJ 3673】可持久化并查集 by zky 用可持久化线段树破之

最后还是去掉异或顺手A了3673,,, 并查集其实就是fa数组,我们只需要维护这个fa数组,用可持久化线段树就行啦 1:判断是否属于同一集合,我加了路径压缩. 2:直接把跟的值指向root[k]的值破之. 3:输出判断即可. 难者不会,会者不难,1h前我还在膜这道题,现在吗hhh就当支持下zky学长出的题了. 3673: #include<cstdio> #include<cstring> #include<algorithm> #define read(x) x=ge

BZOJ 1116 [POI2008]CLO(并查集)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1116 [题目大意] Byteotia城市有n个towns,m条双向roads.每条road连接两个不同的towns, 没有重复的road.你要把其中一些road变成单向边使得:每个town都有且只有一个入度 [题解] 我们发现当一个连通块边数大于等于其点数的时候就满足条件, 那么此题如果出现边数少于点数的连通块就不成立, 用并查集能够完成判断. [代码] #include <cstd

bzoj 1455 可并堆+并查集

一个堆和一个并查集对应,并且满足并查集中所有没有死的人等于堆中的人 1 /************************************************************** 2 Problem: 1455 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:2688 ms 7 Memory:32336 kb 8 *************************************************

【BZOJ】3673: 可持久化并查集 by zky &amp; 3674: 可持久化并查集加强版(可持久化线段树)

http://www.lydsy.com/JudgeOnline/problem.php?id=3674 http://www.lydsy.com/JudgeOnline/problem.php?id=3673 双倍经验啦啦啦.. 给主席树换了个名称果然高大上... 首先要可持久化并查集其实就是可持久化数组... 那么因为数组的形式是这样的$P[x]$,那么我们用一种数据结构实现查找x返回对应的$P[x]$即可啦啦啦. 然后那么我所学的可持久化目前只有主席树QAQ哪天去写写fhqtreap...