zoukankan      html  css  js  c++  java
  • 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);
    }
    
  • 相关阅读:
    表单制作注意事项
    【学习笔记】SIFT尺度不变特征 (配合UCF-CRCV课程视频)
    【图片匹配】--- SIFT_Opencv3.1.0_C++_ubuntu
    【LeetCode】297. Serialize and Deserialize Binary Tree
    【LeetCode】树的遍历
    【LeetCode 33】Search in Rotated Sorted Array
    ubuntu14.04 + GTX980ti + cuda 8.0 ---Opencv3.1.0(基础+opecv_contrib)配置
    [完美方案+无懈可击]ubuntu 14.04(LTS) + GTX 980Ti显卡配置
    OpenCV2.4.9 + Ubuntu15.04配置
    每天学点linux命令之locate 与 find 命令
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/12719745.html
Copyright © 2011-2022 走看看