Factory
Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 132768/132768 K (Java/Others)
Total Submission(s): 399 Accepted Submission(s): 138
Problem Description
我们将A省简化为由N个城市组成,某些城市之间存在双向道路,而且A省的交通有一个特点就是任意两个城市之间都能通过道路相互到达,且在不重复经过城市的情况下任意两个城市之间的到达方案都是唯一的。聪明的你一定已经发现,这些城市构成了树这样一个结构。
现在百度陆续开了许许多多的子公司。每家子公司又会在各城市中不断兴建属于该子公司的办公室。
由于各个子公司之间经常有资源的流动,所以公司员工常常想知道,两家子公司间的最小距离。
我们可以把子公司看成一个由办公室组成的集合。那么两个子公司A和B的最小距离定义为min(dist(x,y))(x∈A,y∈B)。其中dist(x,y)表示两个办公室之间的最短路径长度。
现在共有Q个询问,每次询问分别在两个子公司间的最小距离。
现在百度陆续开了许许多多的子公司。每家子公司又会在各城市中不断兴建属于该子公司的办公室。
由于各个子公司之间经常有资源的流动,所以公司员工常常想知道,两家子公司间的最小距离。
我们可以把子公司看成一个由办公室组成的集合。那么两个子公司A和B的最小距离定义为min(dist(x,y))(x∈A,y∈B)。其中dist(x,y)表示两个办公室之间的最短路径长度。
现在共有Q个询问,每次询问分别在两个子公司间的最小距离。
Input
第一行一个正整数T,表示数据组数。
对于每组数据:
第一行两个正整数N和M。城市编号为1至N,子公司编号为1至M。
接下来N-1行给定所有道路的两端城市编号和道路长度。
接下来M行,依次按编号顺序给出各子公司办公室所在位置,每行第一个整数G,表示办公室数,接下来G个数为办公室所在位置。
接下来一个整数Q,表示询问数。
接下来Q行,每行两个正整数a,b(a不等于b),表示询问的两个子公司。
【数据范围】
0<=边权<=100
1<=N,M,Q,工厂总数<=100000
对于每组数据:
第一行两个正整数N和M。城市编号为1至N,子公司编号为1至M。
接下来N-1行给定所有道路的两端城市编号和道路长度。
接下来M行,依次按编号顺序给出各子公司办公室所在位置,每行第一个整数G,表示办公室数,接下来G个数为办公室所在位置。
接下来一个整数Q,表示询问数。
接下来Q行,每行两个正整数a,b(a不等于b),表示询问的两个子公司。
【数据范围】
0<=边权<=100
1<=N,M,Q,工厂总数<=100000
Output
对于每个询问,输出一行,表示答案。
Sample Input
1
3 3
1 2 1
2 3 1
2 1 1
2 2 3
2 1 3
3
1 2
2 3
1 3
Sample Output
1
0
0
题意:
求树上最短路。
思路:
差不多裸的LCA,这里就当总结LCA的倍增实现了。
dfs求出每个节点的深度及到根节点的距离,并记录每个节点的第 (1<<i) 个祖先,这里和RMQ思想类似;
求LCA时先找到较深的节点(设为 u)与较浅的节点(设为 v)同高度的祖先,在由上至下找两节点第一个不同的祖先,则上一个祖先就是 u、v 的祖先。
代码:
#include<iostream> #include<cstdio> #include<vector> #include<cstring> using namespace std; const int maxn = 1e5+5, INF = 2e9+7; class Edge { public: int u, v, w; Edge(int a, int b, int c):u(a), v(b), w(c) { } }; int n, m, q; int deep[maxn], up[maxn][20], dis[maxn]; vector<Edge> edges; vector<int> grap[maxn], factory[maxn]; void AddEdge(int u, int v, int w) { grap[u].push_back(edges.size()); edges.push_back(Edge(u, v, w)); grap[v].push_back(edges.size()); edges.push_back(Edge(v, u, w)); } void Init() { cin>>n>>m; edges.clear(); memset(up, 0, sizeof(up)); for(int i=0; i<=n; ++i) { dis[i]=0; factory[i].clear(); grap[i].clear(); deep[i]=0; } for(int i=1; i<n; ++i) { int u, v, w; cin>>u>>v>>w; AddEdge(u, v, w); } for(int i=1; i<=m; ++i) { int r, a; cin>>r; while(r--) { cin>>a; factory[i].push_back(a); } } } void Dfs(int u, int fa, int dep, int d) { deep[u]=dep; dis[u]=d; for(int i=1; i<20; ++i)//u 的第(1<<i)个祖先是 up[u][i-1] 的第(1<<(i-1))个祖先 up[u][i]=up[up[u][i-1]][i-1]; for(int i=0; i<grap[u].size(); ++i) { Edge& e=edges[grap[u][i]]; if(e.v==fa) continue; up[e.v][0]=u; Dfs(e.v, u, dep+1, d+e.w); } } int LCA(int u, int v) { if(deep[u] < deep[v]) swap(u, v); int k=deep[u]-deep[v]; for(int i=0; i<20; ++i)//到同一高度 if((1<<i)&k) u=up[u][i]; if(u!=v) { for(int i=19; i>=0; --i) { if(up[u][i]!=up[v][i]) { u=up[u][i]; v=up[v][i]; } } u=up[u][0]; } return u; } int GetDist(int u, int v) { int lca=LCA(u, v); return dis[u]+dis[v]-2*dis[lca]; } void Solve() { Dfs(1, -1, 0, 0); cin>>q; while(q--) { int a, b; cin>>a>>b; int ans=INF; for(int i=0; i<factory[a].size(); ++i) { if(ans==0) break; for(int j=0; j>factory[b].size(); ++j) { if(ans==0) break; if(factory[a][i]==factory[b][j]) { ans=0; break; } } } for(int i=0; i<factory[a].size(); ++i) { if(ans==0) break; for(int j=0; j<factory[b].size(); ++j) { if(ans==0) break; int MinDist=GetDist(factory[a][i], factory[b][j]); ans=min(ans, MinDist); } } cout<<ans<<endl; } } int main() { int t; cin>>t; while(t--) { Init(); Solve(); } return 0; }