bzoj2959: 长跑(LCT+并查集)

题解

动态树Link-cut tree(LCT)总结

LCT常数大得真实

没有环,就是\(lct\)裸题吧

有环,我们就可以绕环转一圈,缩点

怎么搞?

当形成环时,把所有点的值全部加到一个点上,用并查集维护加到哪个点上

判断连通性再用一个并查集

Code

#include<bits/stdc++.h>

#define LL long long
#define RG register

using namespace std;
template<class T> inline void read(T &x) {
    x = 0; RG char c = getchar(); bool f = 0;
    while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
    while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
    x = f ? -x : x;
    return ;
}
template<class T> inline void write(T x) {
    if (!x) {putchar(48);return ;}
    if (x < 0) x = -x, putchar('-');
    int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
    for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}
const int N = 150010;
struct node {
    int v, s, fa, ch[2];
    bool rev;
}t[N];
int S[N], top;
#define lson (t[x].ch[0])
#define rson (t[x].ch[1])
void pushup(int x) {t[x].s = (t[lson].s + t[rson].s + t[x].v);return ;}
void putrev(int x) {
    swap(lson, rson);
    t[x].rev ^= 1;
    return ;
}
void pushdown(int x) {
    if (t[x].rev) {
        if (lson) putrev(lson);
        if (rson) putrev(rson);
        t[x].rev = 0;
    }
}
int fa[N], Fa[N];
int find(int x) {
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}
int Find(int x) {
    return Fa[x] == x ? x : Fa[x] = Find(Fa[x]);
}
bool isroot(int x) {
    return (t[find(t[x].fa)].ch[0] != x) && (t[find(t[x].fa)].ch[1] != x);
}

void rotate(int x) {
    int y = find(t[x].fa), z = find(t[y].fa), k = t[y].ch[1] == x;
    if (!isroot(y)) t[z].ch[t[z].ch[1] == y] = x;
    t[x].fa = z;
    t[t[x].ch[k^1]].fa = y, t[y].ch[k] = t[x].ch[k^1];
    t[x].ch[k^1] = y; t[y].fa = x;
    pushup(y);
    return ;
}

void splay(int x) {
    S[top = 1] = x;
    for (RG int i = x; !isroot(i); i = find(t[i].fa)) S[++top] = find(t[i].fa);
    for (RG int i = top; i; i--) pushdown(S[i]);
    while (!isroot(x)) {
        int y = find(t[x].fa), z = find(t[y].fa);
        if (!isroot(y))
            (t[y].ch[1] == x) ^ (t[z].ch[1] == y) ? rotate(x) : rotate(y);
        rotate(x);
    }
    pushup(x);
    return ;
}

void access(int x) {
    for (int y = 0; x; y = x, x = find(t[x].fa))
        splay(x), t[x].ch[1] = y, pushup(x);
}

void makeroot(int x) {
    access(x); splay(x); putrev(x);
    return ;
}

void link(int x, int y) {
    makeroot(x);
    t[x].fa = y;
}

void split(int x, int y) {
    makeroot(x), access(y), splay(y);
    return ;
}
int a[N], n, m;

void dfs(int x, int y) {
    fa[x] = y;
    pushdown(x);
    if (lson) dfs(lson, y);
    if (rson) dfs(rson, y);
    return ;
}

int main() {
    read(n), read(m);
    for (int i = 1; i <= n; i++) read(a[i]), fa[i] = Fa[i] = i, t[i].s = t[i].v = a[i];
    while (m--) {
        int x, y, op;
        read(op), read(x), read(y);
        if (op == 1) {
            x = find(x), y = find(y);
            if (x == y) continue;
            if (Find(x) != Find(y)) {
                link(x, y);
                Fa[Find(y)] = Find(x);
            }
            else {
                split(x, y);
                t[y].v = t[y].s;
                dfs(y, y);
                t[y].ch[0] = 0;
            }
        }
        else if (op == 2) {
            int tx = find(x);
            splay(tx);
            t[tx].s += y - a[x];
            t[tx].v += y - a[x];
            a[x] = y;
        }
        else {
            x = find(x), y = find(y);
            if (Find(x) != Find(y)) {
                puts("-1");
                continue;
            }
            split(x, y);
            printf("%d\n", t[y].s);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zzy2005/p/10324298.html

时间: 2024-08-30 04:40:05

bzoj2959: 长跑(LCT+并查集)的相关文章

bzoj2959: 长跑 LCT+并查集+边双联通

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=2959 题解 调了半天,终于调完了. 显然题目要求是求出目前从 \(A\) 到 \(B\) 的可以经过重复的点(权值只算一次)的最长路. 考虑把无向图边双缩点以后,形成一棵树的关系.可以发现,边双内的点的点权是可以都取到的.所以边双缩点以后直接求出树上两个点之间的点权之和就可以了. 但是考虑怎么维护这个边双. 新链接一条边的时候,如果两个端点不连通,那么直接连上就可以了. 如果两个端点联通,那

2959: 长跑|LCT+并查集

慎入-此人代码自带5倍常数.. 静态的话就是随便搞出一棵生成树来,然后把环缩起来,询问的答案就是路径上的权值和 动态的就需要LCT来维护生成树,每遇到连起边来就形成环的情况时,就把这个环缩成一个点 动态的查询一条链上的权值和. 为什么我的代码的常数这么大--.后几个点在本地跑5s #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cst

bzoj4998 星球联盟 LCT + 并查集

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4998 题解 根据题意,就是要动态维护点双,求出一个点双的权值和. 所以这道题就是和 bzoj2959 长跑 一样了,每一次枚举一条路径上的点双暴力和合并,并查集维护. 本来是想再练一练 LCT 码力的,结果又调了半天. 大概错误都有: connect 函数的 \(fa\) 和 \(o\) 的顺序写错: 每一次把一条链合并成一个点双的时候需要把代表点的孩子删掉!!! 要把连边和 dfs 一起写

【BZOJ4998】星球联盟 LCT+并查集

[BZOJ4998]星球联盟 Description 在遥远的S星系中一共有N个星球,编号为1…N.其中的一些星球决定组成联盟,以方便相互间的交流.但是,组成联盟的首要条件就是交通条件.初始时,在这N个星球间有M条太空隧道.每条太空隧道连接两个星球,使得它们能够相互到达.若两个星球属于同一个联盟,则必须存在一条环形线路经过这两个星球,即两个星球间存在两条没有公共隧道的路径.为了壮大联盟的队伍,这些星球将建设P条新的太空隧道.这P条新隧道将按顺序依次建成.一条新轨道建成后,可能会使一些星球属于同一

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

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

【BZOJ2594】水管局长加强版,LCT+并查集+二分查找位置

Time:2016.05.10 Author:xiaoyimi 转载注明出处谢谢 传送门 思路: LCT维护路径最小值 倒叙处理询问,就相当于往图里面加边. 实时维护最小值,即最小生成树,可以参照魔法森林. 最初的最小生成树操作用kruskal 最蛋疼的是处理询问时你不知道要删除哪条边,这给kruskal带来很大麻烦,所以我们对原来的每一条边使其编号小的端点在前,大的在后,然后以左端点为第一关键字,右端点为第二关键字排序,记录下每个左端点的所在区间,然后就可以通过二分查找的方式来确定是哪条边了

bzoj2959: 长跑

1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #define maxn 150005 7 using namespace std; 8 9 int n,m,val[maxn],tot[maxn],fa[maxn],sum[maxn],son[maxn][2],bel[maxn],b

小结:并查集

复杂度: O(n*α(n)) 其中α(x),对于x=宇宙中原子数之和,α(x)不大于4 .(对于nocow里的复杂度我也是醉了) 概要: 并查集就是一个数组和一行话. 应用: 图的连通.集合操作.生成树的合并等 技巧及注意: 并查集是个好东西. 维护区间+前缀和:对于一些连续的区间,我们要判断这些区间是否合法,带修改.这种题我们可以考虑并查集来维护区间,用前缀和维护信息,而在并查集按秩合并的时候,一定要注意合并前和合并后如何维护信息,比如这题[BZOJ]1202: [HNOI2005]狡猾的商人

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

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