Description
Y岛风景美丽宜人,气候温和,物产丰富。Y岛上有N个城市,有N-1条城市间的道路连接着它们。每一条道路都连接某两个城市。幸运的是,小可可通过这些道路可以走遍Y岛的所有城市。神奇的是,乘车经过每条道路所需要的费用都是一样的。小可可,小卡卡和小YY经常想聚会,每次聚会,他们都会选择一个城市,使得3个人到达这个城市的总费用最小。 由于他们计划中还会有很多次聚会,每次都选择一个地点是很烦人的事情,所以他们决定把这件事情交给你来完成。他们会提供给你地图以及若干次聚会前他们所处的位置,希望你为他们的每一次聚会选择一个合适的地点。
Input
第一行两个正整数,N和M。分别表示城市个数和聚会次数。
后面有N-1行,每行用两个正整数A和B表示编号为A和编号为B的城市之间有一条路。
城市的编号是从1到N的。再后面有M行,每行用三个正整数表示一次聚会的情况:小可可所在的城市编号,小卡卡所在的城市编号以及小YY所在的城市编号。
Output
一共有M行,每行两个数Pos和Cost,用一个空格隔开。
表示第i次聚会的地点选择在编号为Pos的城市,总共的费用是经过Cost条道路所花费的费用。
Sample Input
1 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
2 4 4
6 6 6
Sample Output
2 5
4 1
6 0
数据范围:
100%的数据中,N<=500000,M<=500000。
40%的数据中N<=2000,M<=2000。
题解Here!
- $p$的深度比$x$小:
相当于$dis(p,a)-dis(x,a)=dis(p,b)-dis(x,b)=dis(x,c)-dis(p,c)=w$。
一共增加了$w$,当然不是最优的。
- $p$的深度比$x$大:
相当于$dis(x,a)-dis(p,a)=dis(p,b)-dis(x,b)=dis(p,c)-dis(x,c)=w$。
即:对于$a$,距离减少了$w$,但是对于$b,c$来说,距离都增加了$w$。
也是一共增加了$w$,当然也不是最优的。
到此,我们找到了到$a,b,c$三个点距离最少的点:
三个$LCA$中,深度最大的那个。
于是距离之和就好办了:
我们知道:
$$dis(u,v)=deep[u]+deep[v]-2 imes deep[LCA(u,v)]$$
而:
$$dis=dis(x,a)+dis(x,b)+dis(x,c)$$
于是:
$$dis=deep[x]+deep[a]-2 imes deep[LCA(x,a)]+deep[x]+deep[b]-2 imes deep[LCA(x,b)]+deep[x]+deep[c]-2 imes deep[LCA(x,c)]$$
把那些$LCA$换掉:
$$dis=3 imes deep[x]+deep[a]+deep[b]+deep[c]-2 imes(deep[x]+deep[x]+deep[y])$$
展开,合并同类项:
$$dis=deep[a]+deep[b]+deep[c]-deep[x]-2 imes deep[y]$$
哇!这不就是:
$$dis=deep[a]+deep[b]+deep[c]-deep[LCA(a,b)]-deep[LCA(b,c)]-deep[LCA(a,c)]$$
好河蟹啊有木有!
于是这题只要再写个树链剖分求$LCA$即可。
现在就怕$NOIP$考这种思维题。。。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #define MAXN 500010 using namespace std; int n,m,c=1; int head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],top[MAXN]; struct Edge{ int next,to; }a[MAXN<<1]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } inline void add(int x,int y){ a[c].to=y;a[c].next=head[x];head[x]=c++; a[c].to=x;a[c].next=head[y];head[y]=c++; } void dfs1(int rt){ son[rt]=0;size[rt]=1; for(int i=head[rt];i;i=a[i].next){ int will=a[i].to; if(!deep[will]){ deep[will]=deep[rt]+1; fa[will]=rt; dfs1(will); size[rt]+=size[will]; if(size[son[rt]]<size[will])son[rt]=will; } } } void dfs2(int rt,int f){ top[rt]=f; if(son[rt])dfs2(son[rt],f); for(int i=head[rt];i;i=a[i].next){ int will=a[i].to; if(will!=fa[rt]&&will!=son[rt])dfs2(will,will); } } int LCA(int x,int y){ while(top[x]!=top[y]){ if(deep[top[x]]<deep[top[y]])swap(x,y); x=fa[top[x]]; } if(deep[x]>deep[y])swap(x,y); return x; } void work(){ int x,y,z,lca1,lca2,lca3,ans,dis; while(m--){ x=read();y=read();z=read(); lca1=LCA(x,y);lca2=LCA(y,z);lca3=LCA(x,z); dis=0; if(lca1==lca2)ans=lca3; else if(lca1==lca3)ans=lca2; else ans=lca1; dis=deep[x]+deep[y]+deep[z]-deep[lca1]-deep[lca2]-deep[lca3]; printf("%d %d ",ans,dis); } } void init(){ int x,y; n=read();m=read(); for(int i=1;i<n;i++){ x=read();y=read(); add(x,y); } deep[1]=1; dfs1(1); dfs2(1,1); } int main(){ init(); work(); return 0; }