牛客练习赛71 F-红蓝图 (kruskal重构树 + 线段树合并)

思路:看到存在删边的维护连通块,首先想到 \(kruskal\) 重构树,我们单独对红边所构成的图跑一次 \(kruskal\) ,顺便记录每次询问的是哪个子树,
并且维护出这颗树的 \(dfs\) 序,这样我们在处理蓝边所构成的图时,就变成查询对应的连续的点了,现在我们倒着处理蓝边,对每个连通块维护一颗动态开点的权值线段树,
将这颗线段树中,对应的该连通块中的点的 \(dfs\) 序赋成 \(1\) ,连通块合并的时候就可以线段树合并维护。

#include <bits/stdc++.h>
using namespace std;
#define lc (rt << 1)
#define rc ((rt << 1) | 1)
#define fi first
#define se second
#define pb push_back
#define pii pair<int, int>
#define rep(i, l, r) for (int i = (l); i <= (r); ++i)
#define per(i, r, l) for (int i = (r); i >= (l); --i)
#define PE(i, u) for (int i = head[u]; i != -1; i = edge[i].next)
typedef long long LL;
const int maxn = 3e6 + 20;
const int mod = 1e9 + 7;

struct Edge
{
    int u, to, w, next;
} edge[maxn * 2], a[maxn];

bool cmp1(Edge A, Edge B){
    if(A.w != B.w){
        return A.w < B.w;
    } else {
        return A.next > B.next;
    }
}

bool cmp2(Edge A, Edge B){
    if(A.w != B.w){
        return A.w < B.w;
    } else {
        return A.next < B.next;
    }
}
int k, head[maxn];
void add(int a, int b){
    edge[k].to = b;
    edge[k].next = head[a];
    head[a] = k++;
}

int fa[maxn], qur[maxn];

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

int tot;
int unit(int x, int y){
    x = Find(x), y = Find(y);
    if(x == y) return 0;
    tot++;
    add(tot, x), add(tot, y);
    fa[x] = fa[y] = fa[tot] = tot;
    return 1;
}

int unit2(int x, int y){
    x = Find(x), y = Find(y);
    if(x == y) return 0;
    return 1;
}

int dfn[maxn], qle[maxn], qri[maxn], id[maxn], tim;
void dfs(int u){
    dfn[u] = qle[u] = ++tim;
    id[tim] = u;
    PE(i, u){
        int to = edge[i].to;
        dfs(to);
    }
    qri[u] = tim;
}

int cntree;
int root[maxn], tree[maxn << 2], ls[maxn << 2], rs[maxn << 2];
void Update(int le, int ri, int pos, int &rt){
    if(!rt) rt = ++cntree;
    if(le == ri){
        tree[rt] = 1;
        return ;
    }
    int mid = (le + ri) >> 1;
    if(pos <= mid) Update(le, mid, pos, ls[rt]);
    if(pos > mid) Update(mid + 1, ri, pos, rs[rt]);
    tree[rt] = tree[ls[rt]] + tree[rs[rt]];
}

int merge(int x, int y){
    if(!x || !y){
    	tree[x | y] = tree[x] + tree[y];
        return x | y;
    }
    // int rt = ++cntree;
    tree[y] = tree[x] + tree[y];
    ls[y] = merge(ls[x], ls[y]);
    rs[y] = merge(rs[x], rs[y]);
    return y;
}
int Query(int le, int ri, int L, int R, int rt){
    if(!rt) return 0;
    if(L <= le && ri <= R){
        return tree[rt]; 
    }
    int mid = (le + ri) >> 1;
    int res = 0;
    if(L <= mid) res += Query(le, mid, L, R, ls[rt]);
    if(R > mid) res += Query(mid + 1, ri, L, R, rs[rt]);
    return res; 
}
int ans[maxn];
int main(int argc, char const *argv[])
{
    int n, m, q;
    scanf("%d%d%d", &n, &m, &q);
    tot = n;
    rep(i, 1, n * 2) head[i] = -1, fa[i] = i;
    rep(i, 1, m){
        int u, v, c;
        scanf("%d%d%d", &u, &v, &c);
        u++, v++;
        a[i] = {u, v, i, c};
    }
    rep(i, 1, q){
        int u, w;
        scanf("%d%d", &u, &w);
        u++;
        a[m + i] = {u, i, w, -1};
    }
    sort(a + 1, a + m + q + 1, cmp1);
    rep(i, 1, m + q){
        if(a[i].next != -1){
            if(a[i].next == 0) unit(a[i].u, a[i].to);
        } else{
            qur[a[i].to] = Find(a[i].u);
        }
    }
    rep(i, 1, tot){
        Find(i);
        if(i == fa[i]){
            dfs(i);
        }
    }
    rep(i, 1, n){
        Update(1, tot, dfn[i], root[i]);
    }
    sort(a + 1, a + m + q + 1, cmp2);
    rep(i, 1, n) fa[i] = i;
    per(i, q + m, 1){
        if(a[i].next != -1){
            if(a[i].next == 1){ 
                int x = a[i].u, y = a[i].to;
                if(unit2(x, y)){ // 这里的unit2只是判断是否可以合并,暂时不合并
                    x = Find(x), y = Find(y);
                    root[x] = root[y] = merge(root[x], root[y]);
                    fa[x] = y; // 在这里完成合并
                }
            }
        } else {
            int u = a[i].u;
            u = Find(u);
            int res = Query(1, tot, qle[qur[a[i].to]], qri[qur[a[i].to]], root[u]);
            ans[a[i].to] = res;
        }
    }
    rep(i, 1, q){
        printf("%d\n", ans[i]);
    }
    return 0;
}

// 3 5 2
// 2 0 1
// 0 1 1
// 2 2 0
// 1 0 0
// 1 1 1
// 1 2
// 2 1

posted @ 2020-10-12 15:22  从小学  阅读(127)  评论(0编辑  收藏  举报