P4185 [USACO18JAN]MooTube G
考察:并查集+离线
和这题有点像的题 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 }