[bzoj 2959] 长跑 link_cut_Tree 维护双联通分量

https://www.lydsy.com/JudgeOnline/problem.php?id=2959
这个垃圾题目我再也不想做了!!!!!!!!!
不但难调还卡常 真的毒瘤 还有实名diss bzoj b z o j 垃圾评测机 交darkbzoj才过的

这个题就发现双联通分量里的点都可以互相到达
每次加边判断连通性
如果已经联通就用并查集缩环为一点
把点权全部加起来即可

虽然讲起来很简单 但是这个题真的很难调啊啊

notice

任何调用 fa f a 数组的时候都要 记得找并查集里的 包括 isroot i s r o o t rel r e l

不想写了 上代码

Cdoes
#include<bits/stdc++.h>
#define For(i, a, b) for(register int i = a; i <= b; ++ i)
#define FOR(i, a, b) for(register int i = a; i >= b; -- i)
#define go(x, i) for(register int i = head[x]; i; i = nxt[i])

using namespace std;

const int N = 3e5 + 10;
int be[N], fa2[N], v[N];
int n, m;

inline int Belong(int x) {
    if(be[x] == x) return x;
    return be[x] = Belong(be[x]);
}

inline int Find(int x) {
    if(fa2[x] == x) return x;
    return fa2[x] = Find(fa2[x]);
}

namespace In  {
# define In_Len 100000000
    static std :: streambuf* fb ( std :: cin.rdbuf ( ) ) ;
    static char buf [In_Len], *ss ( 0 ) ;

    void init ( )  {  fb -> sgetn ( ss = buf, In_Len ) ;  }

    inline int read ( )  {
        register int x ;
        bool opt ( 1 ) ;
        while ( isspace ( *ss ) )  ++ ss ;
        if ( *ss == 45 )  { ++ ss ; opt = 0 ; }
        for ( x = -48 + *ss ; isdigit ( * ++ ss ) ; ( x *= 10 ) += *ss - 48 ) ; ++ ss ;
        return opt ? x : -x ;
    }
# undef In_Len
}

namespace Out  {
# define Out_Len 100000000
    static std :: streambuf* fb ( std :: cout.rdbuf ( ) ) ;
    static char buf [Out_Len], *ss ( buf ) ;

    inline void print ( register int x )  {
        static int T [30], tp ( 0 ) ;
        if ( ! x )  {  *ss ++ =  48 ; *ss ++ = 10 ; return ;  }
        if ( x < 0 )  {  *ss ++ = 45 ; x = -x ;  }
        while ( x ) T [++ tp] = x % 10 | 48, x /= 10 ;
        while ( tp )  *ss ++ = T [tp --] ;
        *ss ++ = 10 ;
    }

    inline void flush ( )  {  fb -> sputn ( buf, ss - buf ) ;  }

# undef Out_Len
}

struct Link_Cut_Tree {
#define ls(x) (ch[x][0])
#define rs(x) (ch[x][1])
#define rel(x) (x == rs(Fa(x)))
#define Fa(x) (Belong(fa[x]))
    int fa[N], ch[N][2], sum[N], val[N], rev[N], Sta[N], top;

    bool isroot(int x) {
        return ls(Fa(x)) != x && rs(Fa(x)) != x;
    }

    void reverse(int x) {
        swap(ls(x), rs(x));
        rev[x] ^= 1;
    }

    void pushup(int x) {
        sum[x] = sum[ls(x)] + sum[rs(x)] + val[x];
    }

    void pushdown(int x) {
        if(rev[x]) {
            if(ls(x)) reverse(ls(x));
            if(rs(x)) reverse(rs(x));
            rev[x] ^= 1;
        }
    }

    void rotate(int x) {
        int dad = Fa(x), f = rel(x);
        if(!isroot(dad)) 
            ch[Fa(dad)][rel(dad)] = x;  
        fa[x] = Fa(dad);
        ch[dad][f] = ch[x][f ^ 1];
        ch[x][f ^ 1] = dad;
        fa[ch[dad][f]] = dad;
        fa[dad] = x;
        pushup(dad), pushup(x);
    }

    void splay(int x) {
        top = 0;
        for(register int i = x; i; i = Fa(i))
            Sta[++ top] = i;
        FOR(i, top, 1)
            pushdown(Sta[i]);
        while(!isroot(x)) 
            rotate(x);
    }

    void access(int x) {
        for(int y = 0; x; x = Fa(y = x)) 
            splay(x), rs(x) = y, pushup(x);
    }

    void makeroot(int x) {
        access(x); splay(x); reverse(x);
    }

    void split(int x, int y) {
        makeroot(x); access(y); splay(y);
    }

    void merge(int x, int y) {
        be[x] = y;
        if(x != y) val[y] += val[x];
        if(ls(x)) merge(ls(x), y);
        if(rs(x)) merge(rs(x), y);
        //ls(x) = rs(x) = 0;
    }

    void link(int x, int y) {
        if(Find(x) != Find(y)) {    
            if(rand() & 2) {
                makeroot(y);
                //cerr << x << ' ' << y << endl;
                fa[y] = x;
            }
            else {
                makeroot(x);
                fa[x] = y;
            }
        }
        else {
            split(x, y);
            merge(y, y);
        }
    }

} T;

void File() {
#ifndef ONLINE_JUDGE
    freopen("2959.in", "r", stdin);
    freopen("2959.out", "w", stdout);
#endif
}

void Init() {
    In::init();
    n = In::read(), m = In::read();
    for(int i = 1; i + 1 <= n; i += 2) {
        v[i] = In::read(); v[i + 1] = In::read();
        T.val[i] = v[i]; T.val[i + 1] = v[i + 1];
        be[i] = fa2[i] = i; be[i + 1] = fa2[i + 1] = i + 1;
    }
    if(n & 1) {
        v[n] = In::read();
        T.val[n] = v[n];
        be[n] = fa2[n] = n;
    }
}

void Solve() {
    int x, y, z;
    For(i, 1, m) {
        x = In::read(), y = In::read(), z = In::read();
    //  cerr << i << ' ' << x << ' ' << y << ' ' << z << endl;
        if(x == 1) {
            int fuck = Belong(y), fk = Belong(z);
            if(fuck == fk)
                continue;
            T.link(fuck, fk);
            fa2[Find(y)] = Find(z);
        }
        if(x == 2) {
            int fuck = Belong(y);
            T.makeroot(fuck);
            T.val[fuck] += (z - v[y]);
            T.pushup(fuck);
            v[y] = z;
        }
        if(x == 3) {
            if(Find(y) != Find(z)) 
                Out::print(-1);
            else {
                int fuck = Belong(y), fk = Belong(z);
                T.split(fuck, fk);
                Out::print(T.sum[fk]);
            }
        }
    }
    Out::flush();
    //cerr << 1.0 * clock() / CLOCKS_PER_SEC << endl;
}

int main() {
    File();
    Init();
    Solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值