2600: [Ioi2011]ricehub
3122: [Sdoi2013]随机数生成器

3124: [Sdoi2013]直径

高橋直樹 posted @ 2013年4月15日 16:39 in BZOJ with tags SDOI bzoj , 1728 阅读

http://www.lydsy.com/JudgeOnline/problem.php?id=3124

RunID User Problem Result Memory Time Language Code_Length Submit_Time
391083 Liu_Ts 3124 Accepted 14000 kb 1572 ms C++ 1383 B 2013-04-15 16:26:40

 

题意

一棵树,求直径和有多少条边一定在直径上。

题解

我就不说我省选有多傻叉了。

首先DFS求出直径,

显然一定在直径上的边一定是任意一条直径上连续的一段。

所以我们遍历求出的直径(红色+蓝色)上的每个点x,

如果以这个点为起点,在不访问直径上的点的情况下,

求出一条最长链,图中的黑色

如果等于蓝色的长度或红色的长度,那么这段蓝色或红色就不是一定在直径上。

这样不断缩小左右端点,剩下的链上的边就是答案

 

#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=200011,M=N*2;
int v[M],w[M],next[M],head[N],k=1;
long long d[N],dep[N],ans1,tmp;
int n,pre[N],A,B,leaf;bool vis[N];

void add(int x,int y,int z)
{
	v[++k]=y;w[k]=z;next[k]=head[x];head[x]=k;
	v[++k]=x;w[k]=z;next[k]=head[y];head[y]=k;
}

void dfs(int x,int fa)
{
	pre[x]=fa;
	if(dep[x]>tmp)
		tmp=dep[x],leaf=x;
	for(int i=head[x];i;i=next[i])
		if(v[i]!=fa)
			dep[v[i]]=dep[x]+w[i],dfs(v[i],x);
}

void  find(int x,int fa)
{
	tmp=max(tmp,d[x]);
	for(int i=head[x];i;i=next[i])
		if((v[i]!=fa) && (!vis[v[i]]))
			d[v[i]]=d[x]+w[i],find(v[i],x);
}

int main()
{
	scanf("%d",&n);int x,y,z;
	for(int i=1;i<n;i++)
		scanf("%d%d%d",&x,&y,&z),add(x,y,z);
	tmp=dep[1]=0;dfs(1,0);A=leaf;
	tmp=dep[A]=0;dfs(A,0);B=leaf;
	ans1=tmp;
	printf("%lld\n",ans1);
	memset(vis,0,sizeof(vis));
	for(int i=B;i;i=pre[i]) vis[i]=1;
	bool flag=1;int left=A,right=B;
	for(int i=B;i;i=pre[i])
	{
		long long ldist=dep[i],rdist=ans1-dep[i];
		tmp=d[i]=0;find(i,0);
		if((tmp==ldist) && flag) 
			left=i,flag=0;
		if(tmp==rdist) right=i;
	}
	int ans2=0;
	for(int i=right;i!=left;i=pre[i]) ans2++;
	printf("%d\n",ans2);
}

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter