把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CF566C】Logistical Questions(点分治)

点此看题面

  • 给定一棵\(n\)个点的树,有点权和边权。
  • 定义两点间的距离为两点间边权和的\(\frac32\)次方。
  • 求这棵树的带权重心。
  • \(n\le2\times10^5\)

下凸函数

显然,对于一条树上路径\([a,b]\)中的点\(x\)\(x\)与另一点\(i\)之间的距离\(d(x,i)\)是一个下凸函数。

考虑对于一个点,\(f(x)=\sum_{i=1}^nd(x,i)\times a(x)\)是凸函数之和,那么它仍然是凸函数。

所以,如果从树的重心向外扩展,\(f(x)\)的变化必然是递增的。

点分治

考虑对于一条链的情况,我们可以采取二分。

把二分扩展到树上,就变成了点分治。注意这里的点分治和通常所说的点分治并不完全一样。

对于当前的根节点,从它向子节点走,最多只会有一个子节点可能使得\(f(x)\)变大。

至于如何判断是否变大,显然不能直接暴力求解。

我们可以求出该函数的导数。设一个子节点的导数为\(p(y)\),总和为\(s\),如果\((s-p(y))-p(y)=s-2p(y)<0\),就说明向这个子节点走可能使得\(f(x)\)变小,那么递归继续做即可。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200000
#define DB double
#define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].v=z)
using namespace std;
int n,a[N+5],ee,lnk[N+5];struct edge {int to,nxt,v;}e[N<<1];
namespace DotSolver//点分治
{
	int ans,rt,Sz[N+5],Mx[N+5],used[N+5];DB cur,res,p[N+5];
	I void GetRt(CI x,CI lst,RI s)//找重心
	{
		Sz[x]=1,Mx[x]=0;for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&
			!used[e[i].to]&&(GetRt(e[i].to,x,s),Sz[x]+=Sz[e[i].to],Mx[x]=max(Mx[x],Sz[e[i].to]));
		Mx[x]=max(Mx[x],s-Sz[x]),Mx[x]<Mx[rt]&&(rt=x);
	}
	I void Calc(CI x,CI lst,CI d,DB& p)//遍历子树进行统计
	{
		cur+=pow(d,1.5)*a[x],p+=1.5*pow(d,0.5)*a[x];//cur统计答案,p统计导数
		for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&(Calc(e[i].to,x,d+e[i].v,p),0);
	}
	I void Work(RI x)//求解当前子树
	{
		RI i,y;DB s=cur=0;used[x]=1;
		for(i=lnk[x];i;i=e[i].nxt) p[y=e[i].to]=0,Calc(y,x,e[i].v,p[y]),s+=p[y];//s统计导数总和
		for((!ans||cur<res)&&(ans=x,res=cur),i=lnk[x];i;i=e[i].nxt)//更新答案
			if(!used[e[i].to]&&s-2*p[y=e[i].to]<0) return GetRt(y,rt=0,Sz[y]),Work(rt);//走向可能使答案变大的点
	}
	I void Solve() {Mx[0]=1e9,GetRt(1,rt=0,n),Work(rt),printf("%d %.10lf\n",ans,res);}
}
int main()
{
	RI i;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",a+i);
	RI x,y,z;for(i=1;i^n;++i) scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
	return DotSolver::Solve(),0;
}

posted @ 2020-11-06 17:38  TheLostWeak  阅读(123)  评论(0编辑  收藏  举报