题目传送门

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

题解

调了半天,终于调完了。


显然题目要求是求出目前从 \(A\) 到 \(B\) 的可以经过重复的点(权值只算一次)的最长路。

考虑把无向图边双缩点以后,形成一棵树的关系。可以发现,边双内的点的点权是可以都取到的。所以边双缩点以后直接求出树上两个点之间的点权之和就可以了。

但是考虑怎么维护这个边双。

新链接一条边的时候,如果两个端点不连通,那么直接连上就可以了。

如果两个端点联通,那么路径上的所有点都会进入同一个边双。可以发现每个点只会进入一次边双,所以可以暴力取出这些点加入边双。

用 LCT 维护就可以了。


说起来可真轻松呢。

但是写起来的细节好多啊:

  1. 对于求出一个点属于哪个边双,可以用一个并查集来维护。这样的话,LCT 上每个点的父亲啊,孩子啊都会受到影响,所以只要用了父亲孩子之类的东西都要在并查集里面跑一遍。一定要注意有没有哪里没有跑并查集。
  2. 判断连通性的时候用 LCT 自带的找根操作太慢了,请用并查集。
  3. 这样下来有了两个并查集呢,不要搞混了。
  4. pushdown!!! pushdown 一定不能漏!!

时间复杂度只有 LCT,所以是 \(o(m\log n)\)。

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;} typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii; template<typename I> inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
} const int N = 1.5e5 + 7; #define lc c[0]
#define rc c[1] int n, m;
int a[N], fa[N], fa2[N], lian[N]; inline int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
inline int find2(int x) { return fa2[x] == x ? x : fa2[x] = find2(fa2[x]); } inline void self_kill() { std::cerr << a[N * 100]; } struct Node { int c[2], fa, rev, s, v, sum; } t[N];
int st[N];
inline bool isroot(int o) { return find2(t[find2(t[o].fa)].lc) != o && find2(t[find2(t[o].fa)].rc) != o; }
inline bool idtfy(int o) { return find2(t[find2(t[o].fa)].rc) == o; }
inline void connect(int fa, int o, int d) { t[fa].c[d] = o, t[o].fa = fa; }
inline void pushup(int o) {
if (find2(o) != o) dbg("o = %d, find2(o) = %d\n", o, find2(o));
if (find2(o) != o) self_kill();
t[o].s = t[t[o].lc].s + t[t[o].rc].s + 1;
t[o].sum = t[t[o].lc].sum + t[t[o].rc].sum + t[o].v;
}
inline void pushdown(int o) {
if (!t[o].rev) return;
if (t[o].lc) t[t[o].lc].rev ^= 1, std::swap(t[t[o].lc].lc, t[t[o].lc].rc);
if (t[o].rc) t[t[o].rc].rev ^= 1, std::swap(t[t[o].rc].lc, t[t[o].rc].rc);
t[o].rev ^= 1;
}
inline void rotate(int o) {
int fa = find2(t[o].fa), pa = find2(t[fa].fa), d1 = idtfy(o), d2 = idtfy(fa), b = t[o].c[d1 ^ 1];
if (!isroot(fa)) t[pa].c[d2] = o; t[o].fa = pa;
connect(o, fa, d1 ^ 1), connect(fa, b, d1);
pushup(fa), pushup(o);
assert(o != t[o].fa);
}
inline void splay(int o) {
if (find2(o) != o) self_kill();
assert(find2(o) == o);
int x = o, tp = 0;
st[++tp] = x;
while (!isroot(x)) st[++tp] = x = find2(t[x].fa);
while (tp) pushdown(st[tp--]);
while (!isroot(o)) {
int fa = find2(t[o].fa);
assert(o != fa);
if (isroot(fa)) rotate(o);
else if (idtfy(o) == idtfy(fa)) rotate(fa), rotate(o);
else rotate(o), rotate(o);
}
}
inline void access(int o) {
assert(find2(o) == o);
for (int x = 0; o; o = find2(t[x = o].fa))
splay(o), t[o].rc = x, pushup(o);
}
inline void mkrt(int o) {
assert(find2(o) == o);
access(o), splay(o);
t[o].rev ^= 1, std::swap(t[o].lc, t[o].rc);
}
inline int getrt(int o) {
assert(find2(o) == o);
access(o), splay(o);
while (pushdown(o), t[o].lc) o = find2(t[o].lc);
return splay(o), o;
}
inline void link(int x, int y) {
assert(find2(x) == x);
assert(find2(y) == y);
mkrt(x);
t[x].fa = y;
}
inline void cut(int x, int y) {
assert(find2(x) == x);
assert(find2(y) == y);
mkrt(x), access(y), splay(y);
if (t[y].lc == x && t[x].rc == 0) t[x].fa = t[y].lc = 0, pushup(y);
} inline void dfs(int o) {
if (!o) return;
pushdown(o);
dfs(find2(t[o].lc));
lian[++lian[0]] = o;
dfs(find2(t[o].rc));
} inline void work() {
while (m--) {
int opt, x, y;
read(opt), read(x), read(y);
if (opt == 1) {
x = find2(x), y = find2(y);
if (find(x) != find(y)) link(x, y), fa[find(x)] = find(y);
else {
mkrt(x), access(y), splay(y);
lian[0] = 0, dfs(y);
for (int i = 1; i < lian[0]; ++i) fa2[lian[i]] = y, t[y].v += t[lian[i]].v;
assert(find2(y) == y);
pushup(y);
}
} else if (opt == 2) splay(find2(x)), t[find2(x)].v += y - a[x], a[x] = y, pushup(find2(x));
else {
x = find2(x), y = find2(y);
if (find(x) != find(y)) puts("-1");
else mkrt(x), access(y), splay(y), printf("%d\n", t[y].sum);
}
}
puts("");
} inline void init() {
read(n), read(m);
for (int i = 1; i <= n; ++i) read(a[i]);
for (int i = 1; i <= n; ++i) t[i].v = a[i], fa[i] = fa2[i] = i, pushup(i);
} int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}

bzoj2959: 长跑 LCT+并查集+边双联通的更多相关文章

  1. BZOJ2959长跑——LCT+并查集(LCT动态维护边双连通分量)

    题目描述 某校开展了同学们喜闻乐见的阳光长跑活动.为了能“为祖国健康工作五十年”,同学们纷纷离开寝室,离开教室,离开实验室,到操场参加3000米长跑运动.一时间操场上熙熙攘攘,摩肩接踵,盛况空前. 为 ...

  2. bzoj2959: 长跑(LCT+并查集)

    题解 动态树Link-cut tree(LCT)总结 LCT常数大得真实 没有环,就是\(lct\)裸题吧 有环,我们就可以绕环转一圈,缩点 怎么搞? 当形成环时,把所有点的值全部加到一个点上,用并查 ...

  3. 【bzoj2959】长跑 LCT+并查集

    题目描述 某校开展了同学们喜闻乐见的阳光长跑活动.为了能“为祖国健康工作五十年”,同学们纷纷离开寝室,离开教室,离开实验室,到操场参加3000米长跑运动.一时间操场上熙熙攘攘,摩肩接踵,盛况空前.为了 ...

  4. BZOJ 2959 长跑 (LCT+并查集)

    题面:BZOJ传送门 当成有向边做的发现过不去样例,改成无向边就忘了原来的思路.. 因为成环的点一定都能取到,我们把它们压成一个新点,权值为环上所有点的权值和 这样保证了图是一颗森林 每次询问转化为, ...

  5. 【bzoj4998】星球联盟 LCT+并查集

    题目描述 在遥远的S星系中一共有N个星球,编号为1…N.其中的一些星球决定组成联盟,以方便相互间的交流.但是,组成联盟的首要条件就是交通条件.初始时,在这N个星球间有M条太空隧道.每条太空隧道连接两个 ...

  6. 【BZOJ2959】长跑 (LCT+并查集)

    Time Limit: 1000 ms   Memory Limit: 256 MB Description 某校开展了同学们喜闻乐见的阳光长跑活动.为了能“为祖国健康工作五十年”,同学们纷纷离开寝室 ...

  7. BZOJ4998星球联盟——LCT+并查集(LCT动态维护边双连通分量)

    题目描述 在遥远的S星系中一共有N个星球,编号为1…N.其中的一些星球决定组成联盟,以方便相互间的交流.但是,组成 联盟的首要条件就是交通条件.初始时,在这N个星球间有M条太空隧道.每条太空隧道连接两 ...

  8. bzoj 2959: 长跑【LCT+并查集】

    如果没有环的话直接LCT 考虑有环怎么办,如果是静态的话就tarjan了,但是这里要动态的缩环 具体是link操作的时候看一下是否成环(两点已联通),成环的话就用并查集把这条链缩到一个点,把权值加给祖 ...

  9. BZOJ 2959: 长跑 LCT_并查集_点双

    真tm恶心...... Code: #include<bits/stdc++.h> #define maxn 1000000 using namespace std; void setIO ...

随机推荐

  1. docker部署Javaweb项目(jdk+tomcat+mysql)

    步骤一:在主机下载安装docker,参照Centos7上安装docker 步骤二:下载Linux版本的JDK1.6和Tomcat6.0(其他项目若依赖其他版本的运行环境可选择另外版本下载),通过sec ...

  2. 2、electron进程

    electron核心我们可以分成2个部分,主进程和渲染进程. 主进程: 主进程连接着操作系统和渲染进程,可以把她看做页面和计算机沟通的桥梁. Electron 运行 package.json 的 ma ...

  3. Java中class的声明

    在Java中下面Class的声明哪些是错误的?(A,B,C)   A:public abstract final class Test { abstract void method();} B:pub ...

  4. 阶段2 JavaWeb+黑马旅游网_15-Maven基础_第1节 基本概念_02maven依赖管理的概念

    传统的web项目jar放在项目中,占用磁盘空间 maven项目里面只保存jar包的坐标.jar包文件都在仓库中.扎包重用都在jar包仓库中.

  5. 配置文件pytest.ini

    前言 pytest配置文件可以改变pytest的运行方式,它是一个固定的文件pytest.ini文件,读取配置信息,按指定的方式去运行. ini配置文件 pytest里面有些文件是非test文件 py ...

  6. Gradle之Gradle 源码分析(四)

    Gradle 的启动 constructTaskGraph runTasks finishBuild gradle 脚本如何编译和执 插件调用流程 一.Gradle 的启动 1.1 整体实现图 1.2 ...

  7. charles抓包教程

    百度搜索下载charles 默认安装即可完成 1.双击charles.exe启动,我的是4.2.7版本.最好下载原版的不要去破解中文,会有不兼容 1.搜索该软件许可证书并输入即可长期使用 2.设置代理 ...

  8. 【ABAP系列】SAP ABAP DYNP_VALUES_UPDATE 更新屏幕字段的函数及用法

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP DYNP_VA ...

  9. spring data jpa Specification 复杂查询+分页查询

    当Repository接口继承了JpaSpecificationExecutor后,我们就可以使用如下接口进行分页查询: /** * Returns a {@link Page} of entitie ...

  10. luogu P1232 [NOI2013]树的计数

    传送门 这题妙蛙 首先考虑构造出一个合法的树.先重新编号,把bfs序整成\(1,2,3...n\),然后bfs序就是按照从上到下从左往右的遍历顺序,所以可以考虑对bfs序分层,可以知道分层方式只会对应 ...