CF566C Logistical Questions 【点分治,求导】

题目描述:设树上两点间距离是最短路径长度的 (1.5) 次方,求带权重心。(边权和点权都有,且为正数)

数据范围:(nle 2 imes 10^5)

众所周知,对于一条路径 ((u,v)) 上的点,到 (x) 的距离是下凸函数。所以 ((u,v)) 上的点到所有点距离的带权和也是下凸函数。所以重心只有一个 (rt) 且向外到所有点距离和越来越大。

所以考虑点分治,每次只需要递归到变小的一棵子树,其他都是更大的。

那变小的子树怎么找呢,暴力显然是不行的。居然可以用求导来做,计算一下各个子树的导数 (p_1,p_2,dots,p_k),那么向第 (i) 棵子树的移动的导数就是 (sum p_j-2p_i),找到唯一一个负数的导数即可,这个的计算时间就是 (O(n)) 的了。总时间复杂度 (O(nlog n))

#include<bits/stdc++.h>
#define Rint register int
using namespace std; 
const int N = 200003;
int n, ans1, a[N], head[N], to[N << 1], nxt[N << 1], w[N << 1];
template<typename T>
inline bool chkmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
template<typename T>
inline bool chkmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline void read(T &x){
	int ch = getchar(); x = 0;
	for(;ch < '0' || ch > '9';ch = getchar());
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
inline void add(int a, int b, int c){
	static int cnt = 0;
	to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt; w[cnt] = c;
}
int siz[N], wson[N], rt, tot;
bool vis[N];
inline void dfs(int x, int f = 0){
	siz[x] = 1; wson[x] = 0;
	for(Rint i = head[x];i;i = nxt[i]) if(to[i] != f && !vis[to[i]]){
		dfs(to[i], x); siz[x] += siz[to[i]]; chkmax(wson[x], siz[to[i]]);
	}
	chkmax(wson[x], tot - siz[x]);
	if(wson[x] <= (tot >> 1)) rt = x;
}
double sum, sumd, d[N], ans2 = 1e20;
inline void calc(int x, int f, int rt, int len){
	double sqr = sqrt(len) * a[x]; sum += len * sqr; d[rt] += sqr;
	for(Rint i = head[x];i;i = nxt[i]) if(to[i] != f) calc(to[i], x, rt, len + w[i]);
}
inline void dac(int x){
	if(vis[x]) return; dfs(x); vis[x] = true; sum = sumd = 0;
	for(Rint i = head[x];i;i = nxt[i]){
		d[to[i]] = 0; calc(to[i], x, to[i], w[i]); sumd += d[to[i]];
	}
	if(chkmin(ans2, sum)) ans1 = x;
	for(Rint i = head[x];i;i = nxt[i])
		if(sumd < d[to[i]] * 2){
			tot = siz[to[i]]; rt = 0; dfs(to[i]); dac(rt); break;
		} 
}
int main(){
	read(n);
	for(Rint i = 1;i <= n;++ i) read(a[i]);
	for(Rint i = 1, u, v, w;i < n;++ i){
		read(u); read(v); read(w);
		add(u, v, w); add(v, u, w);
	}
	tot = n; dfs(1); dac(rt);
	printf("%d %.10lf
", ans1, ans2);
}
原文地址:https://www.cnblogs.com/AThousandMoons/p/12719745.html