题目链接
https://www.luogu.com.cn/problem/P4185
思路
第 v i v_{i} vi个视频推荐列表中将推荐的视频数实际上就是缩点之后该点所在联通块的点的数量。
因此,我们考虑使用并查集。
我们可以采用离线做法。先将每一对关系按照相关性 r i r_{i} ri按照从大到小排序,之后将所有的询问按照 k i k_{i} ki的值从大到小排序。
这样,我们先将 k i k_{i} ki的值大的使用并查集进行缩点,并记录答案。这样从大到小的处理,不会影响后面答案的计算。
时间复杂度: O ( n l o g 2 n ) O(nlog_{2}n) O(nlog2n)
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5;
int n, q;
struct Edge
{
int a, b, r;
bool operator<(const Edge & x) {
return r > x.r;
}
};
vector<Edge>edge;
struct node
{
int k, v, id, ans;
} p[N];
struct DSU {
std::vector<int> f, siz;
DSU() {}
DSU(int n) {
init(n);
}
void init(int n) {
f.resize(n);
std::iota(f.begin(), f.end(), 0);
siz.assign(n, 1);
}
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x) {
return siz[find(x)];
}
};
void solve()
{
cin >> n >> q;
DSU dsu(n + 1);
for (int i = 1, a, b, r; i < n; i++)
{
cin >> a >> b >> r;
edge.push_back({a, b, r});
}
sort(edge.begin(), edge.end());
for (int i = 1; i <= q; i++)
{
cin >> p[i].k >> p[i].v;
p[i].id = i;
p[i].ans = 0;
}
sort(p + 1, p + 1 + q, [&](node x, node y) {return x.k > y.k;});
int idx = 0;
for (int i = 1; i <= q; i++)
{
while (idx < edge.size() && edge[idx].r >= p[i].k)
{
dsu.merge(edge[idx].a, edge[idx].b);
idx++;
}
p[i].ans = dsu.siz[dsu.find(p[i].v)] - 1;
}
sort(p + 1, p + 1 + q, [&](node x, node y) {return x.id < y.id;});
for (int i = 1; i <= q; i++)
{
cout << p[i].ans << endl;
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
// cin >> test;
for (int i = 1; i <= test; i++)
{
solve();
}
return 0;
}