记$d(x,y)$为$x$到$y$的距离,$cost_{x}=sum_{i=1}^{n}w_{i}d(x,i)^{frac{3}{2}}$为$x$的代价
取$C$为足够大量,对于一条边权为$w$的边,在边上新建$wC-1$个点,这些点点权为0(即本身无影响),并将边拆成$wC$段,那么每一段边权为$frac{1}{C}$
任取(新树中)一条路径${a_{1},a_{2},...,a_{l}}$,则$cost_{a_{i}}$具有凸性
根据凸性定义,即求证$forall 2le jle l-1,2cost_{a_{j}}le cost_{a_{j-1}}+cost_{a_{j+1}}$
代入其式子,不妨对每一个$i$都保证此性质,也即求证$2d(a_{j},i)^{frac{3}{2}}le d(a_{j-1},i)^{frac{3}{2}}+d(a_{j+1},i)^{frac{3}{2}}$
对$i$的位置分类讨论:
1.$i$距离$a_{j}$最近,则$d(a_{j},i)le d(a_{j-1},i),d(a_{j+1},i)$,显然成立
2.$i$不距离$a_{j}$最近,记$x=d(a_{j},i)$,那么即$2x^{frac{3}{2}}le (x-frac{1}{C})^{frac{3}{2}}+(x+frac{1}{C})^{frac{3}{2}}$
令$f(x)=x^{frac{3}{2}}$,代入并移项,也即$f(x)-f(x-frac{1}{C})le f(x+frac{1}{C})-f(x)$
当$C$足够大时,两者可以看作$frac{f'(x)}{C}$和$frac{f'(x+frac{1}{C})}{C}$,注意到$f'(x)=frac{3}{2}x^{frac{1}{2}}$单调递增,即成立
进而考虑其中非新建的点${b_{1},b_{2},...,b_{k}}$,根据$cost_{a_{i}}$的凸性,$cost_{b_{i}}$的形式必然是先严格单调递减、再有若干个相同的数(最小值)、最后严格单调递增(首尾两段允许为空)
由于(原树中)任意一条路径都可以被以此法选择,即所有路径都为此形式
(以下删去上述新建的点,即仍考虑原树)
约定$x$为质心当且仅当$forall 1le ile n,cost_{x}le cost_{i}$,题目即要找到任意一个质心即可
任取一点$x$,对与$x$相邻的点$y$,至多存在一个$y$满足$cost_{y}<cost_{x}$
(否则任取其中两个记为$y_{1}$和$y_{2}$,考虑$y_{1}$到$y_{2}$的路径即矛盾)
进一步的,再对其分类讨论:
1.如果不存在$y$满足$cost_{y}<cost_{x}$,则对于树上任意一点$z$,考虑其到$x$的路径,由于最后一步仍没有递增,根据路径的形式不难得到$cost_{x}le cost_{z}$,也即$x$为质心
2.如果存在$y$满足$cost_{y}<cost_{x}$,那么对于以$y$为根时$x$子树中的任意一点$z$,考虑其到$y$的路径,由于最后一步严格单调递减,根据路径的形式不难得到$cost_{y}<cost_{z}$,也即$z$一定不为质心
综上,不难得到下述暴力做法——
初始连通子树$T_{0}=T$,并任取$T_{0}$中一点$x$,求出所有与$x$相邻的点$y$的答案,若不存在$y$满足$cost_{y}<cost_{x}$则$x$为质心,若存在$y$满足$cost_{y}<cost_{x}$(必然唯一)令$T_{0}$为删去$x$后$y$所在的连通块并递归即可(第二种情况,剩下的部分一定不包含质心)
如果令$x$为$T_{0}$中的重心(指通常的定义),那么每一次$T_{0}$的规模至少缩小一半,即至多$o(log n)$层
但如果暴力计算所有$y$,仍会导致复杂度过高(每一次计算是$o(n)$的),考虑优化:
对于所有边$(x,y,w)$,在边上新建一个点,其点权为0、到$x$距离为1、到$y$距离为$w-1$
由此,只需要求出这些新建点的$cost$即可(注意其并不影响上述分析),维护(以$x$为根后)每一个新建点子树内所有点到其距离的$frac{3}{2}$次之和、到其距离+2的$frac{3}{2}$次之和即可
(注意虽然重心一定在$T_{0}$中,但递归的仍是整个$T$)
时间复杂度为$o(nlog n)$,可以通过
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 200005 4 struct Edge{ 5 int nex,to,len; 6 }edge[N<<1]; 7 int n,E,rt,x,y,z,a[N],head[N],sz[N],vis[N]; 8 void add(int x,int y,int z){ 9 edge[E]=Edge{head[x],y,z}; 10 head[x]=E++; 11 } 12 void get_sz(int k,int fa){ 13 sz[k]=1; 14 for(int i=head[k];i!=-1;i=edge[i].nex) 15 if ((!vis[edge[i].to])&&(edge[i].to!=fa)){ 16 get_sz(edge[i].to,k); 17 sz[k]+=sz[edge[i].to]; 18 } 19 } 20 void get_rt(int k,int fa,int s){ 21 int mx=s-sz[k]; 22 for(int i=head[k];i!=-1;i=edge[i].nex) 23 if ((!vis[edge[i].to])&&(edge[i].to!=fa)){ 24 get_rt(edge[i].to,k,s); 25 mx=max(mx,sz[edge[i].to]); 26 } 27 if (mx<=(s>>1))rt=k; 28 } 29 double get_s(int k,int fa,int s){ 30 double sum=a[k]*(pow(s,1.5)-pow(s+2,1.5)); 31 for(int i=head[k];i!=-1;i=edge[i].nex) 32 if (edge[i].to!=fa)sum+=get_s(edge[i].to,k,s+edge[i].len); 33 return sum; 34 } 35 double get_cost(int k,int fa=0,int s=0){ 36 double sum=a[k]*pow(s,1.5); 37 for(int i=head[k];i!=-1;i=edge[i].nex) 38 if (edge[i].to!=fa)sum+=get_cost(edge[i].to,k,s+edge[i].len); 39 return sum; 40 } 41 int dfs(int k){ 42 get_sz(k,0); 43 get_rt(k,0,sz[k]); 44 int pos=0; 45 double s=1; 46 for(int i=head[rt];i!=-1;i=edge[i].nex){ 47 double ss=get_s(edge[i].to,rt,edge[i].len-1); 48 if (ss<s)pos=edge[i].to,s=ss; 49 } 50 if ((!pos)||(get_cost(rt)<=get_cost(pos)))return rt; 51 vis[rt]=1; 52 return dfs(pos); 53 } 54 int main(){ 55 scanf("%d",&n); 56 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 57 memset(head,-1,sizeof(head)); 58 for(int i=1;i<n;i++){ 59 scanf("%d%d%d",&x,&y,&z); 60 add(x,y,z),add(y,x,z); 61 assert(z>=1); 62 } 63 x=dfs(1); 64 printf("%d %.7f ",x,get_cost(x)); 65 return 0; 66 }