#树形dp,直径#51nod 1812 树的双直径


题目

给定一棵树,边权是整数 \(c_i\) ,找出两条不相交的链(没有公共点),

使得链长的乘积最大(链长定义为这条链上所有边的权值之和,如果这条链只有1个点则链长视为0)。

\(n\leq 4*10^5,|c_i|\leq 10^9\)


分析

首先考虑一条直径就是维护最大值和次大值,

然后换根就可以得到经过任意一点的直径 \(f[x]\)

但是两条直径还要考虑不相交,那么分类讨论一下。

如果直径处在两个不同的子树内,可以提前处理 \(dp[x]\) 表示 \(x\) 的子树内 \(f[x]\) 的最大值。

对于 \(x\),求出 \(\max\{dp[y]*dp[y']\}\) 即可

否则,直径一定可以被表示成一条经过 \(x\) 且不经过 \(y\),一条在以 \(y\) 为根的子树内,也就是 \(dp[y]\)

\(x\) 这部分挺难求的,任意一点直径却并没有保证它不经过 \(y\),所以还要维护第三大值,剔除掉 \(y\) 的贡献,再贡献给 \(y\)

最后如果要卡常,就只在答案这里开 __int128 就可以了。

心路历程:提交了16次,一开始根本没发现可以处在两个不同的子树内,而且对求第三大值犹豫再三。

而且调题真的很难受,没求处在两个不同的子树内的时候,只有一个点是RE的(玄学,令根为 \(\left\lceil\frac{n}{2}\right\rceil\) 就会少点RE)

但是其实调题的时候做法还不够完整,因为这么做,我第五个点是WA的,接着就一直查错,然后发现了这种情况,一开始想复杂了,后来利用 \(\max\{dp[y]*dp[y']\}\) 就可以了


代码

#include 
#include 
using namespace std;
const int N=400011; typedef long long lll;
struct node{int y,w,next;}e[N<<1]; __int128 ans;
lll Mn[N],Mx[N],mn[N][3],mx[N][3]; int et=1,as[N],n;
int iut(){
    int ans=0,f=1; char c=getchar();
    while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
    while (isdigit(c)) ans=ans*10+c-48,c=getchar();
    return ans*f;
}
void print(__int128 ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
void Max(lll &a,lll b){a=a>b?a:b;}
void Min(lll &a,lll b){a=ab?a:b;}
void dfs1(int x,int fa){
	for (int i=as[x],flag;i;i=e[i].next)
	if (e[i].y!=fa){
		dfs1(e[i].y,x);
		flag=1;
		for (int k=0;k<3&&flag;++k)
		for (int j=k;j<3&&flag;++j)
		if (mn[x][j]>mn[e[i].y][k]+e[i].w){
			for (int _j=2;_j>j;--_j) mn[x][_j]=mn[x][_j-1];
			mn[x][j]=mn[e[i].y][k]+e[i].w,flag=0;
		}
		flag=1;
		for (int k=0;k<3&&flag;++k)
		for (int j=k;j<3&&flag;++j)
		if (mx[x][j]j;--_j) mx[x][_j]=mx[x][_j-1];
			mx[x][j]=mx[e[i].y][k]+e[i].w,flag=0;
		}
		ans=max(ans,(__int128)Mn[x]*Mn[e[i].y]);
		ans=max(ans,(__int128)Mx[x]*Mx[e[i].y]);
		Min(Mn[x],Mn[e[i].y]),Max(Mx[x],Mx[e[i].y]);
	}
	Min(Mn[x],mn[x][0]+mn[x][1]);
	Max(Mx[x],mx[x][0]+mx[x][1]);
}
void dfs2(int x,int fa){
	lll MN[3],MX[3];
	for (int i=0;i<3;++i) MN[i]=mn[x][i],MX[i]=mx[x][i];
	for (int i=as[x],flag;i;i=e[i].next)
	if (e[i].y!=fa){
		for (int j=0;j<3;++j) mn[x][j]=MN[j],mx[x][j]=MX[j];
		flag=1;
		for (int k=0;k<3&&flag;++k)
		for (int j=k;j<3&&flag;++j)
		if (mn[x][j]==mn[e[i].y][k]+e[i].w){
			for (int _j=j;_j<2;++_j) mn[x][_j]=mn[x][_j+1];
			mn[x][2]=flag=0;
		}
		flag=1;
		for (int k=0;k<3&&flag;++k)
		for (int j=k;j<3&&flag;++j)
		if (mx[x][j]==mx[e[i].y][k]+e[i].w){
			for (int _j=j;_j<2;++_j) mx[x][_j]=mx[x][_j+1];
			mx[x][2]=flag=0;
		}
		flag=1;
		for (int k=0;k<3&&flag;++k)
		for (int j=k;j<3&&flag;++j)
		if (mn[e[i].y][j]>mn[x][k]+e[i].w){
			for (int _j=2;_j>j;--_j) mn[e[i].y][_j]=mn[e[i].y][_j-1];
			mn[e[i].y][j]=mn[x][k]+e[i].w,flag=0;
		}
		flag=1;
		for (int k=0;k<3&&flag;++k)
		for (int j=k;j<3&&flag;++j)
		if (mx[e[i].y][j]j;--_j) mx[e[i].y][_j]=mx[e[i].y][_j-1];
			mx[e[i].y][j]=mx[x][k]+e[i].w,flag=0;
		}
		ans=max(ans,(__int128)Mn[e[i].y]*(mn[x][0]+mn[x][1]));
		ans=max(ans,(__int128)Mx[e[i].y]*(mx[x][0]+mx[x][1]));
		dfs2(e[i].y,x);
	}
}
int main(){
	n=iut();
	for (int i=1;i>1,0),dfs2((n+1)>>1,0);
	print(ans);
	return 0;
}

相关