BZOJ1832: [AHOI2008]聚会

BZOJ1832: [AHOI2008]聚会

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

6 4
1 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
2 4 4
6 6 6

Sample Output

5 2
2 5
4 1
6 0

数据范围:
100%的数据中,N<=500000,M<=500000。
40%的数据中N<=2000,M<=2000。

题解Here!

首先,应该能看出来和$LCA$跑不了关系。。。
然后开始简化问题:
如果是两个点,那么好办,直接$LCA$即可。
然后推到三个点:
我们发现我们要求一个点,使得这个点到给定的三个点的距离之和最小。
有一个结论:这个点一定在给定三个点的互达的路径上。
还有个结论:给定的三个点的两两的$LCA$中,一定至少有两个是相同的,并且是深度最小的那个。
这两个结论相信有基础的$OIer$们都能画几个图然后自行$YY$出来。。。
然后考虑$LCA$对路径的影响:
设$LCA(a,b)=x$,且$x$是深度最大的那个$LCA$。
那么$dis(x,a)+dis(x,b)$一定是最小的。
并且根据上面的结论,如果设$LCA(a,c)=LCA(b,c)=y$,那么$y$的深度一定比$x$小。
并且$dis(x,a)+dis(x,b)+dis(x,c)$一定是最小的。
证明?如下:
假设最优的点为$p$,那么有两种情况:
  • $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\times deep[LCA(u,v)]$$

而:

$$dis=dis(x,a)+dis(x,b)+dis(x,c)$$

于是:

$$dis=deep[x]+deep[a]-2\times deep[LCA(x,a)]+deep[x]+deep[b]-2\times deep[LCA(x,b)]+deep[x]+deep[c]-2\times deep[LCA(x,c)]$$

把那些$LCA$换掉:

$$dis=3\times deep[x]+deep[a]+deep[b]+deep[c]-2\times(deep[x]+deep[x]+deep[y])$$

展开,合并同类项:

$$dis=deep[a]+deep[b]+deep[c]-deep[x]-2\times 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\n",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;
}

 

posted @ 2018-11-04 20:35  符拉迪沃斯托克  阅读(81)  评论(0编辑  收藏
Live2D