bzoj 2959: 长跑 (LCT 维护双联通分量.)

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

三种操作,

修建一条路,

修改点的权值,

从a走到b的权值和.

 

我们用 LCT来做.

首先我们用LCT来动态加边,

如果两个点不连接,那么我们直接连上就可以了,

如果两个点已经连接了,那么我们有 access 这条路径,splay 一下, 然后把路径上的点的权值加到一个点上,这个用并查集维护.

也就是说 splay 中的所有点都会指向一个代表节点. 然后把代表节点的左右儿子清空, 所有节点的父亲我们会在以后的 access中更新.

然后我们用LCT的维护连通性会超时, 所以我们还要在开一个 并查集,这个并查集维护连通性.

 

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 100;
int a[N],b[N],g[N],ff[N];
int find(int k){
    return g[k] == k ? k : g[k] = find(g[k]);
}
int findf(int k){
    return ff[k] == k ? k : ff[k] = findf(ff[k]);
}
void unin(int x, int y){
    int xx = findf(x), yy =findf(y);
    if (xx != yy) ff[xx] = yy;
}

namespace LCT{
    int f[N], sum[N], val[N], rev[N], hep[N], ch[N][2];
    inline int isroot(int x) {
        return ch[f[x]][0] != x && ch[f[x]][1] != x;
    }
    inline void pushup(int x) {
        sum[x] = sum[ch[x][1]] + sum[ch[x][0]] + val[x];
    }
    inline void filp(int x) {
        swap(ch[x][0], ch[x][1]);
        rev[x] ^= 1;  //标记表示已经翻转了该点的左右儿子
    }
    inline void pushdown(int x){
        if (rev[x]){
            if (ch[x][0]) filp(ch[x][0]);
            if (ch[x][1]) filp(ch[x][1]);
            rev[x] ^= 1;
        }
           }
    inline void rotate(int x){
        int y = f[x], z = f[y], k = ch[y][1] == x, v = ch[x][!k];
        if (!isroot(y)) ch[z][ch[z][1] == y] = x;//Attention if()
        ch[x][!k] = y; ch[y][k] = v;
        if (v) f[v] = y; 
        f[y] = x, f[x] = z;
        pushup(y); pushup(x);
    }
    inline void splay(int x){
        int y = x, top = 0; hep[++top] = y;
        while(!isroot(y)) hep[++top] = y = f[y];  //这里个splay 不同之处,用一个栈存下来节点.
        while(top) pushdown(hep[top--]);    //然后一个一个的从上往下pushdown .
        while(!isroot(x)){
            y = f[x]; top = f[y];
            if (!isroot(y))
                rotate((ch[y][0] == y) ^ (ch[top][0] == y)?x:y);
            rotate(x);
        }
        pushup(x);
    }

    inline void access(int x){ 
        for(int y = 0; x; y = x, f[x] = find(f[x]), x = f[x])
            splay(x), ch[x][1] = y, pushup(x);
    }
    inline void makeroot(int x){ //函数功能:把x拎成原图的根
        access(x); splay(x); filp(x);
    }
    inline void dfs(int u, int fa){
        g[u] = fa;
        if (ch[u][0]) dfs(ch[u][0],fa);
        if (ch[u][1]) dfs(ch[u][1],fa);
    }
    inline void link(int x,int y){
        if (findf(x) == findf(y)){
            makeroot(x); access(y); splay(y);
            dfs(y,y); 
            val[y] = sum[y];
            ch[y][0] = ch[y][1] = 0;
            return;
        }
        makeroot(x); access(y); splay(y);
        f[x] = y;
    }

};
int n,m,x,y,op;
int main() {
    int xx,yy;
    scanf("%d%d",&n,&m);
    for (int i = 1; i <= n; ++i) {
        scanf("%d",&LCT::val[i]),a[i] = LCT::val[i],g[i] = i,ff[i] = i;
        LCT::sum[i] = a[i];
    }

    for (int i = 1; i <= m; ++i){
        scanf("%d%d%d",&op,&x,&y);
        if (op == 1){
            xx = find(x); yy = find(y);
            LCT::link(xx,yy);
            unin(xx,yy);
        } else if (op == 2){
            int tmp = y - a[x];
            a[x] = y; xx = find(x);
            LCT::splay(xx);
            LCT::val[xx] += tmp;
            LCT::sum[xx] += tmp;
        } else {
            xx = find(x); yy = find(y);
            if (findf(xx) != findf(yy)){
                puts("-1");
            } else{
                LCT::makeroot(xx); 
                LCT::access(yy); 
                LCT::splay(yy);
                printf("%d\n",LCT::sum[yy]);
            }
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值