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