牛客练习赛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