Codeforces 题目传送门 & 洛谷题目传送门

神仙题 %%%

首先考虑对这个奇奇怪怪的 \(t^{3/2}\) 进行一番观察。考虑构造函数 \(f(x)=ax^{3/2}+b(d-x)^{3/2}\),其中 \(a,b,d\) 为参数,将其求个导 \(f'(x)=\dfrac{3}{2}(ax^{1/2}-b(d-x)^{1/2})\),不难发现 \(f'(0)=-\dfrac{3}{2}b\sqrt{d}<0,f'(d)=\dfrac{3}{2}a\sqrt{d}>0\),而在 \([0,d]\) 中随着 \(x\) 的增大,\(ax^{1/2}\) 随之增大,\(b(d-x)^{1/2}\) 随之减小,故 \(f'(x)\) 在 \([0,d]\) 中为严格增函数,即 \(\exist x_0\in[0,d],f'(x_0)=0\),故 \(f(x)\) 在 \([0,d]\) 中为严格下凸函数

这能给我们什么启示呢?考虑将这东西与树联系起来,对于树上任意一条边 \((u,v)\),假设 \(w_u=a,w_v=b\),边的距离为 \(d\),并且我们这个带权重心不一定刚好落在顶点上——它可以落在任意一条边的任意一点,那么假设我们取了 \((u,v)\) 边上一点,距离 \(u\) 为 \(x\),这样 \(u,v\) 两点的权值之和恰好就是上文中提到的函数 \(f(x)\),而 \(f(x)\) 为严格下凸函数可知必定存在 \((u,v)\) 上一点 \(t\),使得当我们将重心定在 \(t\) 时候,\(u,v\) 两点贡献的权值之和最小,并且离 \(t\) 越远,\(u,v\) 两点贡献的权值之和最大

将这个结论扩展到整棵树依旧成立,我们还是假设重心可以落在任意一条边的任意一点,那么对于重心 \(G\),我们在不断远离 \(G\) 的过程中,所有点贡献权值之和必然是不断增大的;反之当我们不断靠近 \(G\) 的过程中所有点贡献的权值之和必然是不断减小的。或者说得更通俗些,现在有一枚棋子,摆放在树的任意一个位置,我们现在要沿着树上的边移动这枚棋子,那么上述结论说的是这样一件事:如果我们正在将这枚棋子向 \(G\) 的方向移动,那么权值之和是处于不断减小的过程中,否则权值之和是处于不断增大的过程中。

我们就考虑根据这个“移动棋子”的思想解决这道题。我们假设现在有一枚棋子摆放在 \(u\) 号节点,对于 \(u\) 的任意一个邻居 \(v\),我们考虑将 \(u\) 沿着 \(v\) 的方向移动会产生什么影响,我们假设 \(u\) 沿着 \(v\) 方向移动了 \(x\) 的距离,\(u\) 到每个节点 \(v\) 的距离为 \(d_v\),那么显然移动完之和权值之和 \(g_v(x)=\sum\limits_{t\in\text{subtree}(v)}a_t(d_t-x)^{3/2}+\sum\limits_{t\notin\text{subtree}(v)}a_t(d_t+x)^{3/2}\)。我们还可以注意到 \(x=0\) 时 \(g_v(x)\) 的值就是将重心定在 \(u\) 点时的权值,而假设重心 \(G\) 就在 \(v\) 的子树(含 \((u,v)\) 的边上),根据上面的结论有我们将 \(u\) 移动到 \(v\) 的过程中权值呈现减小的趋势,那么必然有 \(g_v(0)\) 的瞬时变化量为负,将 \(g_v(x)\) 求个导可得 \(g_v'(x)=-\sum\limits_{t\in\text{subtree}(v)}\dfrac{3}{2}a_t(d_t-x)^{1/2}-\sum\limits_{t\notin\text{subtree}(v)}\dfrac{3}{2}a_t(d_t+x)^{1/2}\),我们对于每个 \(v\) 进行一遍 DFS 求出 \(g'_v(0)\) 的值,然后找到 \(g_v'(0)<0\) 的 \(v\) 并将棋子移动到 \(v\) 即可,根据之前的推论这样的 \(v\) 是唯一的。

这样复杂度是平方的,过不去,等同于暴力。不过考虑有个东西叫点分治,假设我们预期将棋子移动到 \(v\) 的位置,那么我们可以确定重心一定在 \(v\) 的子树(包括 \((u,v)\) 的边上),我们就每次找出 \(v\) 子树的重心 \(G\)(这里的重心就是普通的重心分治里的重心),将棋子移动到 \(G\) 即可,根据重心分治的复杂度可知这样是 \(n\log n\) 的。

最后注意要每移动一次更新一次答案,不要直接输出最后一次到达的位置。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=2e5;
const int INF=0x3f3f3f3f;
int n,a[MAXN+5],hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],val[MAXN*2+5],ec=0;
void adde(int u,int v,int w){to[++ec]=v;val[ec]=w;nxt[ec]=hd[u];hd[u]=ec;}
bool vis[MAXN+5];int siz[MAXN+5],mx[MAXN+5],cent=0;
int ans=0;double mn=1e25,sum[MAXN+5],sum1=0,sum2=0;
void findcent(int x,int f,int totsiz){
mx[x]=0;siz[x]=1;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f||vis[y]) continue;
findcent(y,x,totsiz);siz[x]+=siz[y];
chkmax(mx[x],siz[y]);
} chkmax(mx[x],totsiz-siz[x]);
if(mx[x]<mx[cent]) cent=x;
}
void calc(int x,int f,int rt,int dep){
sum1+=1.0*a[x]*dep*sqrt(dep);
sum2+=1.0*a[x]*sqrt(dep)*3/2;
sum[rt]+=1.0*a[x]*sqrt(dep)*3/2;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];if(y==f) continue;
calc(y,x,rt,dep+z);
}
}
void divcent(int x){
if(vis[x]) return;vis[x]=1;sum1=sum2=0;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];sum[y]=0;calc(y,x,y,z);
}
if(sum1<mn) mn=sum1,ans=x;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];
if(sum2-sum[y]*2.0<0){
cent=0;findcent(y,x,siz[y]);divcent(cent);
return;
}
}
}
int main(){
scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1,u,v,w;i<n;i++) scanf("%d%d%d",&u,&v,&w),adde(u,v,w),adde(v,u,w);
mx[0]=INF;cent=0;findcent(1,0,n);divcent(cent);
printf("%d %.15lf\n",ans,mn);
return 0;
}

Codeforces 566C - Logistical Questions(点分治)的更多相关文章

  1. 【CF566C】Logistical Questions 点分

    [CF566C]Logistical Questions 题意:给你一棵n个点的树,点有点权,边有边权,两点间的距离为两点间的边权和的$3\over 2$次方.求这棵树的带权重心. $n\le 200 ...

  2. Codeforces 833D Red-Black Cobweb [点分治]

    洛谷 Codeforces 思路 看到树上路径的统计,容易想到点分治. 虽然只有一个限制,但这个限制比较麻烦,我们把它拆成两个. 设黑边有\(a\)条,白边有\(b\)条,那么有 \[ 2a\geq ...

  3. Codeforces 1045G AI robots [CDQ分治]

    洛谷 Codeforces 简单的CDQ分治题. 由于对话要求互相看见,无法简单地用树套树切掉,考虑CDQ分治. 按视野从大到小排序,这样只要右边能看见左边就可以保证互相看见. 发现\(K\)固定,那 ...

  4. Codeforces 848C Goodbye Souvenir [CDQ分治,二维数点]

    洛谷 Codeforces 这题我写了四种做法-- 思路 不管做法怎样,思路都是一样的. 好吧,其实不一样,有细微的差别. 第一种 考虑位置\(x\)对区间\([l,r]\)有\(\pm x\)的贡献 ...

  5. Codeforces 938G Shortest Path Queries [分治,线性基,并查集]

    洛谷 Codeforces 分治的题目,或者说分治的思想,是非常灵活多变的. 所以对我这种智商低的选手特别不友好 脑子不好使怎么办?多做题吧-- 前置知识 线性基是你必须会的,不然这题不可做. 推荐再 ...

  6. Codeforces 526F Pudding Monsters - CDQ分治 - 桶排序

    In this problem you will meet the simplified model of game Pudding Monsters. An important process in ...

  7. Codeforces 448C Painting Fence:分治

    题目链接:http://codeforces.com/problemset/problem/448/C 题意: 有n个木板竖着插成一排栅栏,第i块木板高度为a[i]. 你现在要将栅栏上所有地方刷上油漆 ...

  8. Sereja and Brackets CodeForces - 380C (线段树+分治思路)

    Sereja and Brackets 题目链接: CodeForces - 380C Sereja has a bracket sequence s1, s2, ..., *s**n, or, in ...

  9. Codeforces 986D Perfect Encoding FFT 分治 高精度

    原文链接https://www.cnblogs.com/zhouzhendong/p/9161557.html 题目传送门 - Codeforces 986D 题意 给定一个数 $n(n\leq 10 ...

随机推荐

  1. vue基础-动态样式&表单绑定&vue响应式原理

    动态样式 作用:使用声明式变量来控制class和style的值 语法: :class/:style 注意:尽可能不要把动态class和静态class一起使用,原因动态class起作用的时间会比较晚,需 ...

  2. UF_CAMGEOM_ask_custom_points 封装缺陷

    如果当前设置为0个点时,取自定义点就会报错,这又是一个封装错误 解决办法,只能是这么搞了:

  3. 脚本注入1(boolean&&get)

    现在,我们回到之前,练习脚本支持的布尔盲注(get型). 布尔盲注的应用场景是查询成功和失败时回显不同,且存在注入点的地方. 这里以Less-8为例: 发现查询成功时,会显示:失败则无回显. 同时发现 ...

  4. STM32定时器学习---基本定时器

    STM32F1系列的产品,除了互联网产品外,工作8个,3种定时器,其中一种就是基本定时器.那么STM32单片机的基本定时器如何操作以及编程呢? 下面我们就来详细的了解一下 STM32F1系列的产品,除 ...

  5. 对dy和Δy的浅薄理解

    一.导数定义 当函数y=f(x)的自变量x在一点x0上产生一个增量Δx时,函数输出值的增量Δy与自变量增量Δx的比值在Δx趋于0时的极限a如果存在,a即为在x0处的导数,记作f'(x0)或df(x0) ...

  6. Python中Numpy及Matplotlib使用

    Python中Numpy及Matplotlib使用 1. Jupyter Notebooks 作为小白,我现在使用的python编辑器是Jupyter Notebook,非常的好用,推荐!!! 你可以 ...

  7. accept error: Too many open files

    今天测试socket服务器同一时间处理多个客户端连接问题,第一次测试1000个的时候没问题,第二次测试1000个服务器accept的时候就报错了 accept error: Too many open ...

  8. Ubuntu 16.04 下 旋转显示器屏幕 竖屏显示

    xrandr -o left $ xrandr -o left 向左旋转90度 $ xrandr -o right 向右旋转90度 $ xrandr -o inverted 上下翻转 $ xrandr ...

  9. max-points-on-a-line leetcode C++

    Given n points on a 2D plane, find the maximum number of points that lie on the same straight line. ...

  10. cf17A Noldbach problem(额,,,素数,,,)

    题意: 判断从[2,N]中是否有超过[包括]K个数满足:等于一加两个相邻的素数. 思路: 枚举. 也可以:筛完素数,枚举素数,直到相邻素数和超过N.统计个数 代码: int n,k; int prim ...