Gravatar
终焉折枝
积分:1649
提交:219 / 388

Pro3598  [NOI 2021]轻重边

更好的阅读体验:https://www.cnblogs.com/To-Carpe-Diem/p/19638717


大意

给定两个点 $a$ 和 $b$,首先对于 $a$ 到 $b$ 路径上的所有点 $x$(包含 $a$ 和 $b$),你要将与 $x$ 相连的所有边变为轻边。然后再将 $a$ 到 $b$ 路径上包含的所有边变为重边。

给定两个点 $a$ 和 $b$,你需要计算当前 $a$ 到 $b$ 的路径上一共包含多少条重边。


思路

我们考虑一个小巧思。

我们将问题可以转化为将 $a \to b$ 这条路径的点染上独一无二的颜色,最终求 $a \to b$ 路径上的重边即为两端颜色相同的边的数量。

因为每次我们选的颜色都不一样,这样操作一定能够让我们通过只记录端点的颜色去统计这个值,我们通过直接维护区间的左右的端点的颜色和相邻重色的个数,我们如果区间的端点颜色重合就可以将这个答案继续累加。这个就是处理本题线段树的方式。

但是对于这个题目是在树上的操作,那就没什么说的了,直接树剖维护即可。但是如果你像我一样闲着没事的话可以用 LCT 维护,原因是这个更改 $a \to b$ 路径的过程可以用 LCT 的 split 操作非常简便的完成。至于 LCT 里面维护的内容,和线段树几乎一样,剩下就没什么了。


代码

#include<iostream>
#include<algorithm>
using namespace std;

#define lc(x) t[x].ch[0]
#define rc(x) t[x].ch[1]
#define fa(x) t[x].fa
const int MAXN = 1e5 + 5;

struct node{
    int ch[2], fa;
    int col, tag;
    int lc, rc;
    int cnt; 
    int sz;
    bool rev;
    node(){
        ch[1] = ch[0] = fa = 0;
        col = tag = lc = rc = cnt = sz = 0;
        rev = false;
    }
}t[MAXN];

bool isroot(int x){
    return (lc(fa(x)) != x) && (rc(fa(x)) != x);
}

void pushup(int x) {
    t[x].sz = t[lc(x)].sz + t[rc(x)].sz + 1;
    t[x].cnt = t[lc(x)].cnt + t[rc(x)].cnt;
    
    t[x].lc = lc(x) ? t[lc(x)].lc : t[x].col;
    t[x].rc = rc(x) ? t[rc(x)].rc : t[x].col;
    
    if(lc(x) && t[lc(x)].rc == t[x].col) t[x].cnt ++;
    if(rc(x) && t[rc(x)].lc == t[x].col) t[x].cnt ++;
}

void update_rev(int x){
    if(!x) return;
    swap(lc(x), rc(x));
    swap(t[x].lc, t[x].rc);
    t[x].rev ^= 1;
}

void apply_col(int x, int c){
    if(!x) return;
    t[x].col = t[x].lc = t[x].rc = t[x].tag = c;
    t[x].cnt = t[x].sz - 1;
}

void pushdown(int x){
    if(t[x].rev){
        update_rev(lc(x));
        update_rev(rc(x));
        t[x].rev = 0;
    }
    if(t[x].tag){
        apply_col(lc(x), t[x].tag);
        apply_col(rc(x), t[x].tag);
        t[x].tag = 0;
    }
}

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

void pushshell(int x){
    if(!isroot(x)) pushshell(fa(x));
    pushdown(x);
}

void splay(int x){
    pushshell(x);
    while(!isroot(x)){
        int y = fa(x), z = fa(y);
        if(!isroot(y)) (rc(z) == y) ^ (rc(y) == x) ? rotate(x) : rotate(y);
        rotate(x);
    }
}

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

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

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

void modify(int u, int v, int c){
    split(u, v);
    apply_col(v, c);
}

void solve(){
    int n, m; cin >> n >> m;
    for(int i = 1;i <= n;i ++){
        t[i] = node();
        t[i].col = t[i].lc = t[i].rc = -i;
        t[i].sz = 1;
    }
    for(int i = 1;i < n;i ++){
        int u, v; cin >> u >> v;
        makeroot(u); fa(u) = v;
    }
    int cur_col = 0;
    while(m --){
        int op, u, v;
        cin >> op >> u >> v;
        if(op == 1){
            modify(u, v, ++ cur_col);
            split(u, v);
            apply_col(v, ++ cur_col);
        }
		else{
            split(u, v);
            cout << t[v].cnt << "\n";
        }
    }
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T; cin >> T;
    while(T --) solve();
    return 0;
}


2026-02-25 21:09:54    
我有话要说
暂无人分享评论!