BZOJ 2870 最长道路tree 题解

题目传送门

题目大意: 给一棵树,每个点有点权,一条路径的贡献为路径上最小的点权 × \times ×路径上的点数,找出贡献最大的路径的贡献。

题解

考虑边分治,重构树时,虚点的点权设为所在子树树根的点权,然后考虑将路径上的点数转化为路径上的边数+1,然后将实边边权设为 1 1 1,虚边边权设为 0 0 0

求解时,每次将重心边左右两棵子树的点分别存下来,并且存下他们到重心边路径上的边权和以及最小点权,分别记在两个数组 f 1 , f 2 f_1,f_2 f1,f2 中,将其中的点按照最小点权从大到小排序,然后对于 f 1 [ i ] f_1[i] f1[i],考虑其作为整条路径上最小点权的情况,此时要在 f 2 f_2 f2 中找到所有点权大于 f 1 [ i ] f_1[i] f1[i] 的点的最大边权和,然后和 f 1 [ i ] f_1[i] f1[i] 凑在一起更新一下答案。

由于 f 1 , f 2 f_1,f_2 f1,f2 都排了序,所以很容易知道 f 2 f_2 f2 中点权小于 f 1 [ i ] f_1[i] f1[i] 的点的最大边权和,然后将 f 1 , f 2 f_1,f_2 f1,f2 反过来再做一次即可,具体看代码吧:

#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 200010 //
#define inf 999999999
#define ll long long

int n,val[maxn],real[maxn];
struct V_E{
	struct edge{int x,y,z,next;bool v;}e[maxn<<1];
	int first[maxn],len;V_E():len(1){}
	void buildroad(int x,int y,int z){e[++len]=(edge){x,y,z,first[x],false};first[x]=len;}
	void ins(int x,int y,int z){buildroad(x,y,z);buildroad(y,x,z);}
}A,B;
void rebuild(int x,int fa)
{
	int last=0,New;
	for(int i=A.first[x];i;i=A.e[i].next)
	{
		int y=A.e[i].y;if(y==fa)continue;
		rebuild(y,x);if(!last)last=x,B.ins(x,y,1);
		else val[New=++n]=val[x],B.ins(last,New,0),B.ins(New,y,1),last=New;
	}
}
int size[maxn],E,vE;
void findE(int x,int fa,int Size)
{
	size[x]=1;
	for(int i=B.first[x];i;i=B.e[i].next)
	{
		int y=B.e[i].y;if(B.e[i].v||B.e[i].y==fa)continue;
		findE(y,x,Size);size[x]+=size[y];
		int mx=max(size[y],Size-size[y]);
		if(mx<vE)vE=mx,E=i;
	}
}
struct par{int mi,len;}a[maxn],b[maxn];ll ans=0;
void work(par *f1,int &t1,par *f2,int &t2,int now_E)
{
	int mx=0;
	for(int i=1,j=1;i<=t1;i++){
		while(j<=t2&&f2[j].mi>=f1[i].mi)mx=max(mx,f2[j].len),j++;
		if(j>1)ans=max(ans,1ll*(mx+f1[i].len+1+B.e[now_E].z)*f1[i].mi);
	}
}
int t1,t2;void dfs(par *f,int &t,int x,int fa,int mi,int dep)
{
	mi=min(mi,val[x]);f[++t]=(par){mi,dep};
	for(int i=B.first[x];i;i=B.e[i].next)
	if(B.e[i].y!=fa&&!B.e[i].v)dfs(f,t,B.e[i].y,x,mi,dep+B.e[i].z);
}
bool cmp(par x,par y){return x.mi>y.mi;}
void solve(int now_E)
{
	dfs(a,t1=0,B.e[now_E].x,0,inf,0);
	dfs(b,t2=0,B.e[now_E].y,0,inf,0);
	sort(a+1,a+t1+1,cmp);sort(b+1,b+t2+1,cmp);
	work(a,t1,b,t2,now_E);work(b,t2,a,t1,now_E);
}
void fenzhi(int now_E,int Size)
{
	B.e[now_E].v=B.e[now_E^1].v=true;solve(now_E);
	int x=B.e[now_E].x,y=B.e[now_E].y;
	int Size1=(size[x]<size[y]?size[x]:Size-size[y]);
	int Size2=(size[y]<size[x]?size[y]:Size-size[x]);
	E=0;vE=inf;findE(B.e[now_E].x,0,Size1);if(E)fenzhi(E,Size1);
	E=0;vE=inf;findE(B.e[now_E].y,0,Size2);if(E)fenzhi(E,Size2);
}
//-----------------------------------------------------------------
inline char cn()
{
	static char buf[1000010],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
//#define cn getchar
template<class TY>void read(TY &x)
{
	x=0;int f1=1;char ch=cn();
	while(ch<'0'||ch>'9'){if(ch=='-')f1=-1;ch=cn();}
	while(ch>='0'&&ch<='9')x=x*10+(ch-'0'),ch=cn(); x*=f1;
}

int main()
{
	read(n); for(int i=1;i<=n;i++)read(val[i]),real[i]=1;
	for(int i=1,x,y;i<n;i++)read(x),read(y),A.ins(x,y,0);rebuild(1,0);
	E=0;vE=inf;findE(1,0,n);fenzhi(E,n);printf("%lld",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值