【BZOJ】【P2870】【最长道路tree】【题解】【点分……树?】

博客介绍了如何使用点分治方法解决BZOJ P2870问题,重点在于如何巧妙地处理子树的链合并,通过将子树分为两堆来优化计算。作者提出了‘点分树’这一概念,并提到这种数据结构可能适用于动态修改和路径查询。虽然题目本身是静态的,但作者暗示这种方法可能激发更多动态树形结构的题目。
摘要由CSDN通过智能技术生成

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2870

看完就想到点分治了

可是用传统的点分治合并两条子树的链的时候很蛋疼……

于是我们换个方式

找到重心后把子树分成两堆,统计过根且起始结束位置在两个不同的堆里的答案

递归两堆子树

这种思想很早就有了 http://hi.baidu.com/345585690/item/fc3d0dd3167c28896cce3f09 http://blog.sina.com.cn/s/blog_76f6777d0101imnn.html

而且也不是很难写

如果把分治过程记录下来就可以做到动态修改询问,不过好像没人给这种数据结构起名字(或者我太弱了不知道)……

我就给它起名叫点分树了……

边分加虚点,括号序列什么的都太神了不会

好像树上点/链修改,整体/子树 路径查询的题不多……(QTREE有几个,wc,zjoi也有)

似乎可以有一大波新题诞生(又要被D了233)?

不过说起来这道题是静态的我在这扯什么蛋……

马上就去写1095

Code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=50010;
int getint(){
	int res=0;char c=getchar();
	while(!isdigit(c))c=getchar();
	while(isdigit(c))res=res*10+c-'0',c=getchar();
	return res;
}
vector<int>G[maxn];
int n,f[maxn],siz[maxn],w[maxn],d[maxn],root,dep[maxn];
int cant[maxn];
long long ans;
void makert(int u,int All,int fa,int &root){
	siz[u]=1;f[u]=0;
	for(int i=0,v;i<G[u].size();i++){
		if((v=G[u][i])==fa||cant[v])continue;
		makert(v,All,u,root);
		siz[u]+=siz[v];
		f[u]=max(f[u],siz[v]);
	}f[u]=max(f[u],All-f[u]);
	if(f[root]>f[u])root=u;
}
int dsize,dsize2,tsize;
pair<int,int> data[maxn],data2[maxn];
pair<int,int> tmp[maxn];
void dfs(int u,int fa){
	data[++dsize]=make_pair(d[u],dep[u]);
	for(int i=0,v;i<G[u].size();i++){
		if((v=G[u][i])==fa||cant[v])continue;
		d[v]=min(w[v],d[u]);dep[v]=dep[u]+1;
		dfs(v,u);
	}
}
int cnt=0;
void solve(int u,int All){ 
	int now=++cnt;
	int root=0;f[root]=n+1;
	makert(u,All,0,root);	 
	makert(root,All,0,u);	
	int sum=0,can=0,used=0;
	for(int i=0,v;i<G[u].size();i++)can+=!cant[v=G[u][i]];
	if(can<=1)return;
	for(int i=0,v;i<G[u].size();i++){
		if(cant[v=G[u][i]])continue;
		sum+=siz[v];cant[v]=now;used++;
		if(sum>=(All+1)/2||used+1==can)break;
	}dsize=0;dep[u]=0;d[u]=w[u];dfs(u,u);dsize2=dsize;
	copy(data+1,data+1+dsize,data2+1);
	for(int i=0,v;i<G[u].size();i++){
		if(cant[v=G[u][i]]&&cant[v]!=now)continue;
		cant[v]=now-cant[v];
	}dsize=0;dep[u]=0;d[u]=w[u];dfs(u,u);tsize=0;
	sort(data+1,data+1+dsize);
	sort(data2+1,data2+1+dsize2);
	int maxy=-1;
	for(int i=dsize;i>=1;i--)
	if(data[i].second>maxy)tmp[++tsize]=data[i],maxy=data[i].second;
	copy(tmp+1,tmp+1+tsize,data+1);dsize=tsize;maxy=-1;tsize=0;
	for(int i=dsize2;i>=1;i--)
	if(data2[i].second>maxy)tmp[++tsize]=data2[i],maxy=data2[i].second;
	copy(tmp+1,tmp+1+tsize,data2+1);dsize2=tsize;
	sort(data+1,data+1+dsize);
	sort(data2+1,data2+1+dsize2);
	for(int i=1;i<=dsize;i++){
		ans=max(ans,(long long)data[i].first*(data[i].second+1));
		pair<int,int> *x=lower_bound(data2+1,data2+1+dsize2,make_pair(data[i].first,-1));
		if(x==data2+1+dsize2)continue;
		ans=max(ans,(long long)(data[i].second+x->second+1)*data[i].first);
	}
	for(int i=1;i<=dsize2;i++){
		ans=max(ans,(long long)data2[i].first*(data2[i].second+1));
		pair<int,int> *x=lower_bound(data+1,data+1+dsize,make_pair(data2[i].first,-1));
		if(x==data+1+dsize)continue;
		ans=max(ans,(long long)(data2[i].second+x->second+1)*data2[i].first);
	}		
	if(sum+1>2)
	solve(u,sum+1);
	for(int i=0,v;i<G[u].size();i++){
		if(cant[v=G[u][i]]&&cant[v]!=now)continue;
		cant[v]=now-cant[v];
	}if(All-sum>2)
	solve(u,All-sum);
	for(int i=0;i<G[u].size();++i)cant[G[u][i]]=cant[G[u][i]]==now?0:cant[G[u][i]];
}
int main(){
	n=getint();
	for(int i=1;i<=n;i++)w[i]=getint();
	for(int i=1;i<n;i++){
		int u=getint(),v=getint();
		G[u].push_back(v);
		G[v].push_back(u);
	}solve(1,n);
	cout<<ans<<endl;
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值