bzoj-3124 直径

题意:

给出一个有n个结点,边有长度的树;

求这个树的直径,以及有多少边在所有的直径上;


题解:

树的直径就不用说了吧。。随便搜一下就可以;

而对于一个边在所有的直径上,等价于删掉这条边得到的两颗树中不存在一条长度等于直径的链;

那么问题就是快速求出删边之后两颗树的直径了;

这里我们采用乱搞大法!

总之其实就是维护一下几个状态,转移随便搞搞式子就OK了;

fs[x][0],fs[x][1],fs[x][2]分别表示从x出发,到某一儿子的子树,的最长链长度,次长链长度,第三长链长度;

ff[x]表示结点x的到它的父树中的最长链长度;

fz[x][0],fz[x][1]分别表示结点x的最长的儿子的子树中最长链长度,和次长的儿子的子树中最长链长度;

fl[x]表示结点x的父树中的最长链长度;

式子啥的贴出来也只是鬼畜,想看就自己到代码里翻?

总之情况考虑要周全,拍一拍就能AC了;

时间复杂度O(n);


代码:


#include
#include
#include
#define N 210000
using namespace std;
typedef long long ll;
int next[N<<1],to[N<<1],val[N<<1],head[N],ce;
int fa[N],fv[N];
ll fs[N][3],ff[N],fl[N],fz[N][2];
void add(int x,int y,int v)
{
	to[++ce]=y;
	val[ce]=v;
	next[ce]=head[x];
	head[x]=ce;
}
void dfs1(int x)
{
	ll temp;
	for(int i=head[x];i;i=next[i])
	{
		if(to[i]!=fa[x])
		{
			fa[to[i]]=x;
			fv[to[i]]=val[i];
			dfs1(to[i]);
			temp=fs[to[i]][0]+val[i];
			if(temp>fs[x][0])
				fs[x][2]=fs[x][1],fs[x][1]=fs[x][0],fs[x][0]=temp;
			else if(temp>fs[x][1])
				fs[x][2]=fs[x][1],fs[x][1]=temp;
			else if(temp>fs[x][2])
				fs[x][2]=temp;
			temp=max(fs[to[i]][0]+fs[to[i]][1],fz[to[i]][0]);
			if(temp>fz[x][0])
				fz[x][1]=fz[x][0],fz[x][0]=temp;
			else if(fs[to[i]][0]+fs[to[i]][1]>fz[x][1])
				fz[x][1]=temp;
		}
	}
}
void dfs2(int x)
{
	if(fa[x])
		ff[x]=max(ff[fa[x]],fs[fa[x]][0]==fs[x][0]+fv[x]?fs[fa[x]][1]:fs[fa[x]][0])+fv[x],
		fl[x]=max(max(fz[fa[x]][0]==max(fz[x][0],fs[x][0]+fs[x][1])?fz[fa[x]][1]:fz[fa[x]][0],fl[fa[x]]),fs[x][0]+fv[x]==fs[fa[x]][0]?
				max(ff[fa[x]]+fs[fa[x]][1],fs[fa[x]][1]+fs[fa[x]][2]):
				(fs[x][0]+fv[x]==fs[fa[x]][1]?
					max(ff[fa[x]]+fs[fa[x]][0],fs[fa[x]][0]+fs[fa[x]][2]):
					max(ff[fa[x]]+fs[fa[x]][0],fs[fa[x]][0]+fs[fa[x]][1])
				));
	for(int i=head[x];i;i=next[i])
	{
		if(to[i]!=fa[x])
		{
			dfs2(to[i]);
		}
	}
}
int main()
{
	int n,m,i,j,k,x,y,v,cnt;
	ll ans;
	scanf("%d",&n);
	for(i=1;i



你可能感兴趣的:(bzoj,图论,搜索,OIer刷题记录,bzoj,树形DP,树的直径)