题目 【并查集】
思路
- >=k即符合条件的边才给它合并,这样的话这个连通块中所有的点都符合条件啦
- 每次询问都重新合并一次的话会超时
- 用到两个快排,把每条边从大到小排,再把询问的k从大到小排
- 先做最大的k,把符合条件的点合并后,那么下一个k肯定比这个小啦
- 那么符合大k的点肯定符合小k的点啦,那么就不用再重复合并啦
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int p[N], num[N], ans[N];
int n, q;
struct E{
int a,b,w;
bool operator<(const E& t) {
return w>t.w;
}
}e[N];
struct Q{
int k,v,i;
bool operator<(const Q& t) {
return k>t.k;
}
}query[N];
int find(int x) {
if(x != p[x]) p[x] = find(p[x]);
return p[x];
}
void merge(int a, int b) {
int pa=find(a), pb=find(b);
if(pa != pb) {
p[pa] = pb;
num[pb] += num[pa];
}
}
int main()
{
cin>>n>>q;
for(int i=0; i<N; ++i) {
p[i] = i;
num[i] = 1;
}
for(int i=0; i<n-1; ++i) {
int a,b,w;
scanf("%d%d%d", &a, &b, &w);
e[i] = {a,b,w};
}
for(int i=0; i<q; ++i) {
int k,v;
scanf("%d%d", &k, &v);
query[i] = {k,v,i};
}
//
sort(e,e+n-1);
sort(query,query+q);
int j=0;
for(int i=0; i<q; ++i) {
auto& qq=query[i];
while(j<n-1 && e[j].w >= qq.k) {//valid merge
merge(e[j].a, e[j].b);
j++;
}
ans[qq.i] = num[find(qq.v)];
}
for(int i=0; i<q; ++i) {
printf("%d\n", ans[i]-1);
}
return 0;
}