题意简述
给定一颗带边权无根树,定义两个点之间的关联度是两点间最短路径的边权最小值
多次询问,每次询问 (v) 和 (k),求有多少个点满足与 (v) 的关联度不小于 (k)。
解题报告
首先想一个不是很暴力的暴力
对于某一个 (k),因为关联度是边权最小值,所以只加入合法的边(边权 (geq k))建一个不一定联通的图,就能删掉不满足的点对。此时图中任意两个联通的点之间都满足关联度 (geq k),统计 (v) 所在连通块大小就能求出答案。
每次要扫一遍所有的边,维护连通性可以用并查集,时间复杂度 (O(qnlog n))
发现加边这个过程其实不需要每次都扫一遍
其实随着 (k) 的逐渐上升,以前加过的边还是会被加,只是会多一些新的边
于是可以将询问离线下来按 (k) 排序,把边按边权排序,然后处理询问的时候每次只加需要的边
时间复杂度 (O(n + qlog n))
代码实现
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#define forall(G,i) for (int i = 0, __for_siz__ = (int) (G).size(); i < __for_siz__; ++i)
#define DEBUG(x) std::cerr << #x << " = " << x << std::endl;
#define ALL(x) (x).begin(), (x).end()
#define MP std::make_pair
#define se second
#define fi first
using std::cin;
using std::cout;
using std::endl;
inline int read() {
int s = 0, x = 1; char ch = getchar();
while (!isdigit(ch)) { if (ch == '-') x = -x; ch = getchar(); }
while (isdigit(ch)) { s = s * 10 + ch - '0'; ch = getchar(); }
return s * x;
}
const int MAXN = 100000 + 10;
// 对于一个 k,某点周围所有满足的点都要有 w <= k
// 因此使用并查集维护连通性,对于每个询问分别加入所有 w <= k 的边
// 然后发现 k 在单调增的时候可以动态更新边集
int n, q;
struct REdge {
int u, v, w;
REdge(int _u = 0, int _v = 0, int _w = 0) :
u(_u), v(_v), w(_w) {}
} es[MAXN], qry[MAXN];
bool cmp1(REdge r1, REdge r2) {
return r1.w > r2.w;
}
bool cmp2(REdge r1, REdge r2) {
return r1.w < r2.w;
}
struct DSU {
int u[MAXN]; int siz[MAXN];
void Init(int n) {
for (int i = 1; i <= n; ++i) siz[i] = 1;
}
int Find(int x) { return !u[x] ? x : u[x] = Find(u[x]); }
int Size(int x) { return siz[Find(x)];}
bool Merge(int x, int y) {
x = Find(x); y = Find(y);
if (x == y) return false;
u[x] = y; siz[y] += siz[x];
siz[x] = 0; return true;
}
} U;
int anss[MAXN];
int main() {
n = read(); q = read();
U.Init(n);
for (int i = 1; i <= n - 1; ++i) {
int u = read(); int v = read(); int w = read();
es[i] = REdge(u, v, w);
}
for (int i = 1; i <= q; ++i) {
int k = read(); int u = read();
qry[i] = REdge(u, i, k);
} std::sort(es + 1, es + 1 + n - 1, cmp1);
std::sort(qry + 1, qry + 1 + q, cmp1);
int ii = 1;
for (int iq = 1; iq <= q; ++iq) {
int k = qry[iq].w;
for (; ii <= n - 1; ++ii) {
if (es[ii].w >= k) {
U.Merge(es[ii].u, es[ii].v);
} else break;
}
anss[qry[iq].v] = U.Size(qry[iq].u);
}
for (int i = 1; i <= q; ++i) printf("%d
", anss[i] - 1);
return 0;
}