并查集

思路:对于每次询问,我们找出所有边权 >=k 的边,对于每个找出的边,对其两端的点进行合并操作,最后 v点所在联通块 所含的点的数目减一(减去自身) 就是本次询问的答案

但因为数据过大,每次询问我们都将 所有 边权>=k 的边找出来 对其两端点进行合并操作的话,会超时
而且还会有许多重复操作:
比如 第一次询问要求我们找出 所有边权>=4 的边,第二次询问要求我们找出 所有边权>=2 的边
你会发现 >=4 的边 其实也是 >=2 的,所以对于第二次询问,其实我们没有必要找出所有 >=2 的边
只需要找出一部分便是(在上一次询问中已经被找到的边没必要再找一次)

所以我们需要一些优化:
根据上面的性质,我们可以先把 q 次询问,按照 k 值从大到小排序,先执行 k 值较大的,再执行较小的,这样就消去了一些重复操作

Code

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5+10;

struct node{
    int a,b,w;
    bool operator < (const node & x) const { 
        return w > x.w;
    }
}p[N];
struct nope{
    int id,k,v;
    bool operator < (const nope & x) const { 
        return k > x.k;
    }
}Q[N];
int fa[N];
int num[N];  // num[i]:i所在连通块 包含的点 的数目
int ans[N];  // ans[i]:第i次询问的答案
int n,q;

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

int main(){
    cin>>n>>q;
    for(int i=1;i<=n;i++) fa[i]=i,num[i]=1;

    for(int i=1;i<=n-1;i++){
        int a,b,w;
        cin>>a>>b>>w;
        p[i]={a,b,w};
    }
    sort(p+1,p+n);

    for(int i=1;i<=q;i++){
        int k,v;
        cin>>k>>v;
        Q[i]={i,k,v};
    }
    sort(Q+1,Q+q+1);

    for(int i=1,j=1;i<=q;i++){
        int id=Q[i].id,k=Q[i].k,v=Q[i].v;
        while(j<=n-1&&p[j].w>=k){
            int a=p[j].a,b=p[j].b;
            a=find(a),b=find(b);
            if(a!=b){
                fa[a]=b;
                num[b]+=num[a];
            }
            j++;
        }
        ans[id]=num[find(v)]-1;
    }

    for(int i=1;i<=q;i++) cout<<ans[i]<<endl;
    return 0;
}