1798: 小Z的城市
Time Limit: 5 Sec Memory Limit: 128 MBSubmit: 60 Solved: 16
[Submit][Status][Web Board]
Description
小Z身为A国的君王,A国的所有城市都归小A所有,A国中从任意一个城市出发都能到达其他任意一个城市,每两个直接相邻的城市xi , yi之间都有一条已知长度的路径相连。A国共有n个城市(编号为1~n),被n-1条无向边相连。小Y和小X的开国功臣,但是小X擅文,小Y好武,所以他们关系很差,现在小Z想在区间[l ,r]的城市中选择两个奖赏这两位开国功臣一人一个城市,彼此厌恶的小X和小Y想让聪明的你飞到A国帮助他们做出选择可以距离彼此更远。
(也就是说最后小X会选择城市idx(l<=idx<=r) , 小Y会选择城市idy(l<=idy<=r) , 保证idx!=idy 而且idx距离idy尽可能远)。
Input
多组数据
每组数据第一行两个正整数n和q 表示A国有n个城市,q次询问 ( 2<=n <=100000 , q<=100000 )
接下来n-1行,每行输入3个正整数xi , yi , di 表示城市xi与yi被一条长度为di的路径相连
(1<=xi , yi<=n , di<=100)
接下来q个询问,每次询问输入一行li和ri,对于每次询问希望在li和ri区间选择两个城市分给小X和小Y,使他们距离最大 (1<=li < ri<=n)
所有数据保证Σn <=500000
Output
每次询问输出一个使小X和小Y距离的最大值
Sample Input
5 1 1 2 1 2 3 2 1 4 3 4 5 4 2 5
Sample Output
10
题解:线段树+lca .. 首先要知道一个这样的东西,假如一棵子树 A和另外一棵子树 B合并成 C,那么C 里面的最远点对是原来的A树里面最远点对和B树里面的最远点对这四个点中间的 2 个.所以知道了这个特性
所有的点下面的子树的最远点对就可以利用线段树进行维护了,树上最远的两个点之间的距离是 dis[u]+dis[v]-2*dis[lca(u,v)]
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef vector<int> vi; const int N = 200005; const int DEG = 25; int n,q; struct Edge { int v,w,next; } edge[N]; int head[N],tot,cost[N]; void addEdge(int u,int v,int w) { edge[tot].v = v,edge[tot].w = w,edge[tot].next = head[u],head[u] = tot++; } void init() { memset(head,-1,sizeof(head)); tot = 0; } /************* lca 调用 dfs(1,0,0) 以及 getanc() ***************/ int anc[N][DEG],dep[N]; void dfs(int u,int fa,int d,int w) { dep[u]=d; anc[u][0]=fa; cost[u] = w; for (int i=head[u]; i!=-1; i=edge[i].next) if (edge[i].v!=fa) dfs(edge[i].v,u,d+1,w+edge[i].w); } void getanc() { for (int i=1; i<DEG; i++) for (int j=1; j<=n; j++) anc[j][i]=anc[anc[j][i-1]][i-1]; } int swim(int u,int H) { int i=0; while (H) { if (H&1) u=anc[u][i]; i++; H>>=1; } return u; } int lca(int u,int v) { if (dep[u]<dep[v]) swap(u,v); u=swim(u,dep[u]-dep[v]); if (u==v) return u; for (int i=DEG-1; i>=0; i--) { if (anc[u][i]!=anc[v][i]) { u=anc[u][i]; v=anc[v][i]; } } return anc[u][0]; } /************ segment tree *****************/ int getdistance(int a,int b) { int _lca = lca(a,b); return cost[a]+cost[b]-2*cost[_lca]; } vi tree[N<<2]; /// tree[i] 管辖的是以 i 为根节点的子树中相聚最远的两点 vi max_dis_point(vi a,vi b) { for(int i=0; i<b.size(); i++) { a.push_back(b[i]); } vi ans; int _max = -1; for(int i=0; i<a.size(); i++) { for(int j=i+1; j<a.size(); j++) { int _distance = getdistance(a[i],a[j]); if(_distance>_max) { _max = _distance; ans.clear(); ans.push_back(a[i]); ans.push_back(a[j]); } } } return ans; } void build(int l,int r,int idx) { if(l==r) { tree[idx].push_back(l); tree[idx].push_back(l); return; } int mid = (l+r)>>1; build(l,mid,idx<<1); build(mid+1,r,idx<<1|1); tree[idx] = max_dis_point(tree[idx<<1],tree[idx<<1|1]); } vi query(int l,int r,int L,int R,int idx) { vi ans; ans.clear(); if (r < L || R < l) return ans; if(l >= L&& r <= R) { return tree[idx]; } else { vi vl,vr; int mid = (l+r)>>1; vl = query(l,mid,L,R,idx<<1); vr = query(mid+1,r,L,R,idx<<1|1); return max_dis_point(vl,vr); } } int main() { while(scanf("%d%d",&n,&q)!=EOF) { init(); for(int i=1; i<=4*n; i++) tree[i].clear(); for(int i=1; i<n; i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); addEdge(u,v,w); addEdge(v,u,w); } dfs(1,0,0,0); getanc(); build(1,n,1); while(q--) { int a,b; scanf("%d%d",&a,&b); vi ans = query(1,n,a,b,1); int res = 0; if(ans.size()==1) { res = getdistance(ans[0],ans[0]); } else res = getdistance(ans[0],ans[1]); printf("%d ",res); } } return 0; }