Codeforces Gym 100015C City Driving 离线LCA
City Driving
题目连接:
http://codeforces.com/gym/100015/attachments
Description
You recently started frequenting San Francisco in your free time and realized that driving in the city is a
huge pain. There are only N locations in the city that interest you, though, and you have decidedtotry
to improve your driving experience. Since you lack a GPS and cannot remember too many di!erent routes,
you wrote down the directions and how long it takes to get between N di!erent pairs of locations (the same
in both directions), ensuring that using only these paths you can get from any location to any other one.
Now you are planning your trip for the weekend and you need to figureoutthefastestwaytogetbetween
Q pairs of locations in the city using only the routes you have written down.
Input
The input consists of multiple test cases. The first line contains a single integer N,3 ! N ! 100,000, the
number of locations of interest and the number of routes you wrotedown.Thenext N lines each contain
three integers u, v,and w (1 ! w ! 1,000), indicating that you have directions from location u to location v
and vice-versa (0-indexed) which take w time. The following line contains a single integer Q,1 ! Q ! 10,000,
the number of pairs of locations you need to compute the travel timefor. Thenext Q lines each contain
two integers u and v, indicating that you should find the minimum time to get from location u to location
v. The input terminates with a line with N = 0. For example:
Output
For each test case, print out Q lines, one for each pair of locations u and v you are finding the fastest routes
for. Each line should simply contain the minimum time it takes to travel from location u to location v.For
example, the correct output for the sample input above would be:
Sample Input
7
0 1 2
0 2 3
1 3 2
2 3 8
2 4 3
3 5 1
1 6 7
3
4 5
0 6
1 2
0
Sample Output
11
9
5
Hint
题意
给你一个n环n边的图,问你两点之间的最短路
题解:
随便找一个环,然后在这个环上随便去掉一条边,然后就比较这个树上的距离小,还是经过这条边的饿距离小
比如你去掉的边是a,b,边权是w,你查询u,v
那么你比较dis(u,v),dis(u,a)+w+dis(b,v),dis(u,b)+w+dis(a,u)就好了
找环上的边,我推荐用并查集
代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
struct node
{
int x,y;
};
vector<node>G[maxn];
int dp[18][maxn*2],dis[maxn],parent[maxn],vis[maxn],pos[maxn],res[maxn];
int n,m,c,num,cnt,si;
int qx=0,qy=0,qv=0;
void init()
{
qx = qy = qv = 0;
cnt = num = si = 0;
memset(dp,0,sizeof(dp));
memset(dis,0,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(res,0,sizeof(res));
memset(pos,0,sizeof(pos));
for(int i=0;i<maxn;i++)
G[i].clear();
}
int Find(int i)
{
if(i!=parent[i])
parent[i]=Find(parent[i]);
return parent[i];
}
void Union(int i,int j)
{
int x,y;
x=Find(i);
y=Find(j);
if(x!=y)
parent[x]=y;
}
void dfs3(int u,int dist)
{
int i,j;
vis[u]=1;
dis[u]=dist;
pos[u]=cnt;
res[si]=u;
dp[0][cnt++]=si++;
for(i=0;i<G[u].size();i++)
{
j=G[u][i].x;
if(!vis[j])
{
dfs3(j,dist+G[u][i].y);
dp[0][cnt++]=dp[0][pos[u]];
}
}
}
void rmq()
{
int i,j,k;
for(i=1;(1<<i)<=n;i++)
for(j=n-1;j>=0;j--)
{
k=(1<<(i-1));
dp[i][j]=dp[i-1][j];
if(k+j<n)
dp[i][j]=min(dp[i][j],dp[i-1][j+k]);
}
}
int cal(int i,int j)
{
int k;
if(i<j)swap(i,j);
k=0;
while((1<<k)<=(i-j+1))
k++;
k--;
k=min(dp[k][j],dp[k][i-(1<<k)+1]);
return res[k];
}
int Dis(int u,int v)
{
int k = cal(pos[u],pos[v]);
return dis[u]+dis[v]-dis[k]*2;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
if(n==0)break;
init();
for(int i=0;i<=n;i++)
parent[i]=i;
for(int i=1;i<=n;i++)
{
int x,y,z;scanf("%d%d%d",&x,&y,&z);
x++,y++;
int p = Find(x),q = Find(y);
if(p==q){
qx = x,qy = y,qv = z;
continue;
}
Union(x,y);
G[x].push_back((node){y,z});
G[y].push_back((node){x,z});
}
for(int i=1;i<=n;i++)
if(!vis[i])
dfs3(i,0);
n=n*2-1;
rmq();
int q;
scanf("%d",&q);
while(q--)
{
int x,y;scanf("%d%d",&x,&y);
x++,y++;
int res = Dis(x,y);
res = min(res,Dis(x,qx)+Dis(y,qy)+qv);
res = min(res,Dis(x,qy)+Dis(y,qx)+qv);
printf("%d\n",res);
}
}
}