MooTube

 

给定一棵n个点的树(n=1e5),有边权, 
两点间距离定义为两点路径上的 
边权最小值。m个询问(m=1e5),k,v, 
询问对于点v,距离>=k的点有多少个(不含v) 

Input

n个点,m个询问

下面n-1行为边的信息

下面m行

ki和vi

Output

 

Sample Input

7 4
1 2 2
1 3 1
3 4 6
3 5 5
2 6 4
2 7 3
2 1  //对于点1,找出距离大于2的点。这里两点间的距离定义为两点间路径上的边权最小值。
4 3
6 5
10 7

Sample Output

3 //对于点1,距离大于2的点有3个
2
0 
0



sol:首先,本题两点间的距离定义为两点间路径上的最小边权。对于某点,我们要找距离大于该点一个值的结点个数。因此,先考虑将边权按从大到小的顺序排列,然后依次将边权大于等于询问距离值的边加进集合,然后看待查询结点所在的集合有多少个元素,答案为当前集合元素个数-1(因为不包含自己)。但对于m个询问,我们不可能每次去这样从头加边的做一遍,其进一步的想法将所有询问也按要求的距离值降序排列就好了。这样我们就可以通过一次不断地加边地操作处理完m个询问。

代码实现:
 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 #define N 100010
 5 int n,m,sz[N],fa[N],ans[N];
 6 struct node{int x,y,z;}a[N],q[N];
 7 inline bool cmp(node x,node y){return x.z>y.z;}
 8 int find(int x){
 9     if(fa[x]!=x) fa[x]=find(fa[x]);
10     return fa[x];
11 }
12 int main(){
13     scanf("%d%d",&n,&m);
14     for(int i=1;i<=n;i++) 
15         fa[i]=i,sz[i]=1;//初始化每个点的父亲结点为自身,该点集合结点个数为1 
16     for(int i=1;i<n;i++) 
17         scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
18     for(int i=1;i<=m;i++) 
19         scanf("%d%d",&q[i].z,&q[i].x),q[i].y=i;
20         //读入询问的边权值,结点号,并将该询问的序号赋值为i 
21     sort(a+1,a+n,cmp);//按边权值从大到小排序 
22     sort(q+1,q+m+1,cmp);//询问按距离降序排列 
23     int tmp=1;
24     for(int i=1;i<=m;i++)//针对M个询问 
25     {
26         while(tmp<n && a[tmp].z>=q[i].z)//将所有大于这个询问要求的边权的边进行合并 
27         {
28             int x=find(a[tmp].x),y=find(a[tmp].y);
29             if(x!=y) fa[x]=y,sz[y]+=sz[x];//以y为父亲的集合结点个数 
30             tmp++;
31         }
32         ans[q[i].y]=sz[find(q[i].x)]-1;//询问的该结点集合结点个数-1 
33     }
34     for(int i=1;i<=m;i++) printf("%d
",ans[i]);
35     return 0;
36 }


原文地址:https://www.cnblogs.com/cutepota/p/12513630.html