并查集
思路:对于每次询问,我们找出所有边权 >=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; }