原题链接

考察:并查集+离线

和这题有点像的题 HDU 3938 (本蒟蒻甚至写过)

思路:

        乍一看似乎是LCA,但是询问两点最短距离必定TLE.我们可以发现两点之间的最短边首先是原题给定一条边,然后再对该边进行延伸.假定当前给定值是k,我们枚举边,将边两端进行合并,这样就是符合条件的结点个数.当k变小,原先已经合并的点必定还满足条件,所以只需要在原基础上再进行合并.

        这里就很像双指针算法了.此时我们优化枚举边的顺序,从大到小枚举即可在O(n)时间求出.

 1 #include <iostream> 
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int N = 100010;
 6 struct Road{
 7     int u,v,w;
 8     bool operator<(const Road& r){
 9         this->w>r.w;
10     }
11 }road[N];
12 struct cmp{
13     bool operator()(Road a,Road b){
14         return a.w>b.w;
15     }
16 };
17 struct Query{
18     int k,v,id;
19     bool operator<(const Query& q){
20         return this->k>q.k;
21     }
22 }query[N];
23 int n,q,p[N],sz[N],ans[N];
24 int findf(int x)
25 {
26     if(p[x]!=x) p[x] = findf(p[x]);
27     return p[x];
28 }
29 void merge(int a,int b)
30 {
31     int pa = findf(a),pb = findf(b);
32     if(pa==pb) return;
33     sz[pb]+=sz[pa];
34     p[pa] = pb;
35 }
36 int main()
37 {
38 //    freopen("in.txt","r",stdin);
39     scanf("%d%d",&n,&q);
40     for(int i=1;i<=n;i++) p[i] = i,sz[i] = 1;
41     for(int i=1;i<n;i++)
42     {
43         int u,v,w; scanf("%d%d%d",&u,&v,&w);
44         road[i] = {u,v,w};
45     }
46     sort(road+1,road+n,cmp());
47     for(int i=1;i<=q;i++)
48     {
49         int k,v; scanf("%d%d",&k,&v);
50         query[i] = {k,v,i};
51     }
52     sort(query+1,query+q+1);
53     for(int i=1,j=1;i<=q;i++)
54     {
55         int k = query[i].k;
56         while(road[j].w>=k)
57         {
58             int a = road[j].u,b = road[j].v;
59             merge(a,b);
60             j++;
61         }
62         ans[query[i].id] = sz[findf(query[i].v)]-1;
63     }
64     for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
65     return 0;
66 }