[ZOJ3195]Design the city
Cerror is the mayor of city HangZhou. As you may know, the traffic system of this city is so terrible, that there are traffic jams everywhere. Now, Cerror finds out that the main reason of them is the poor design of the roads distribution, and he want to change this situation.
In order to achieve this project, he divide the city up to N regions which can be viewed as separate points. He thinks that the best design is the one that connect all region with shortest road, and he is asking you to check some of his designs.
Now, he gives you an acyclic graph representing his road design, you need to find out the shortest path to connect some group of three regions.
Input
The input contains multiple test cases! In each case, the first line contian a interger N (1 < N < 50000), indicating the number of regions, which are indexed from 0 to N-1. In each of the following N-1 lines, there are three interger Ai, Bi, Li (1 < Li < 100) indicating there's a road with length Li between region Ai and region Bi. Then an interger Q (1 < Q < 70000), the number of group of regions you need to check. Then in each of the following Q lines, there are three interger Xi, Yi, Zi, indicating the indices of the three regions to be checked.
Process to the end of file.
Output
Q lines for each test case. In each line output an interger indicating the minimum length of path to connect the three regions.
Output a blank line between each test cases.
Sample Input
4 0 1 1 0 2 1 0 3 1 2 1 2 3 0 1 2 5 0 1 1 0 2 1 1 3 1 1 4 1 2 0 1 2 1 0 3
Sample Output
3 2 2 2
其实读完题目的时候我是无语的。。。第一次看到那么具体的地名(杭州)所以杭州的片区是一个DAG?(雾
好吧不扯了,其实这道题还是挺简单的,套用lca的模板就行了。
求树上连接三个节点的最短路(单源最短路走起!(手动滑稽 )
好吧,既然是树上的最短路,那首选LCA啊,单元最短路怕是会直接MLE
(我才不会说这是在LCA专题里的题目)
找三个点的公共祖先的过程很显而易见,先找两个点的最近公共祖先,然后找这个祖先和第三个点的最近公共祖先就是这三个点的最近公共祖先了。这里就不作严谨证明了。
参照两个点的最短路公式(dist[u]+dist[v]-2*dist[anc])
我撸了一个dist[u]+dist[v]+dist[w]-3*dist[anc] (这里懒得多开一个变量就把前面用的w当第三个点了)然后漂亮得。。。没过样例。
好的,经过冷静思考,我们会发现这个方法只适用于三个点与根节点的连线交与同一点才行。
所以要特判一下,如果其中两个点先交汇,再与第三个点交汇,那么要多减去一个前两点公共祖先到三点公共祖先的距离。
也就是要做4次lca,得出三点中两两的公共祖先 anc1,anc2,anc3和三点的最近公共祖先anc。若有某个ancx不等于anc,那么,就要再原公式上减掉一个 dist[ancx]-dist[anc]
验证了正确性后,开心地提交了,然后开心地TLE了。
后来换了求法,就A了,写博客的时候突发奇想卡了一波常数然后这个方法就AC了。
其实求这个不需要那么麻烦,直接把三点间两两最短路相加,这时的值刚好是要求路的两倍,于是除二得答案,只需要做三次lca就行了。
前面的做法就保留在注释里了(套进源代码要循环展开卡常数才能AC)
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 50005
using namespace std;
inline void read(int &x)
{
x=0;int f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
x*=f;
}
int N,Q;
struct node{
int to,nex,w;
}edge[maxn<<1];
int head[maxn],tot;
inline void insert(int from,int to,int w)
{
++tot;
edge[tot].nex=head[from];
edge[tot].to=to;
head[from]=tot;
edge[tot].w=w;
}
int anc[maxn][16],depth[maxn],dist[maxn];
void init()
{
for(int j=1;j<=15;j++)
for(int i=1;i<=N;i++)
anc[i][j]=anc[anc[i][j-1]][j-1];
}
void dfs(int v,int u)
{
anc[v][0]=u;
depth[v]=depth[u]+1;
for(int i=head[v];i;i=edge[i].nex)
{
if(edge[i].to!=u)
{
dist[edge[i].to]=dist[v]+edge[i].w;
dfs(edge[i].to,v);
}
}
}
int lca(int u,int v)
{
if(depth[u]<depth[v])
swap(u,v);
for(int i=15;i>=0;i--)
if(depth[anc[u][i]]>=depth[v])
u=anc[u][i];
if(u==v)
return u;
for(int i=15;i>=0;i--)
if(anc[u][i]!=anc[v][i])
{
u=anc[u][i];
v=anc[v][i];
}
return anc[u][0];
}
int main()
{
int u,v,w;
bool flag;
while(scanf("%d",&N)!=EOF)
{
for(int i=1;i<N;i++)
{
read(u);read(v);read(w);
u++;v++;
insert(u,v,w);
insert(v,u,w);
}
dist[1]=1;
dfs(1,1);
init();
read(Q);
depth[0]=-1;
if(flag)
printf("\n");
flag=1;
for(int i=1;i<=Q;i++)
{
read(u);read(v);read(w);
u++;v++;w++;
int ancs1=lca(u,v);
int ancs2=lca(u,w);
int ancs3=lca(v,w);
int ans1=dist[u]+dist[v]-2*dist[ancs1];
int ans2=dist[u]+dist[w]-2*dist[ancs2];
int ans3=dist[v]+dist[w]-2*dist[ancs3];
int ans=ans1+ans2+ans3;
ans/=2;
printf("%d\n",ans);
/*
int ancs1=lca(u,v);
int ancs2=lca(u,w);
int ancs3=lca(v,w);
int ancs=lca(ancs1,ancs2);
int ans=dist[u]+dist[v]+dist[w]-3*dist[ancs];
if(ancs1!=ancs)
ans=ans+dist[ancs]-dist[ancs1];
if(ancs2!=ancs)
ans=ans+dist[ancs]-dist[ancs2];
if(ancs3!=ancs)
ans=ans+dist[ancs]-dist[ancs3];
printf("%d\n",ans);
*/
}
memset(head,0,sizeof(head));
tot=0;
}
}