根据题意,发现题目中的图,其实就是一颗树或者是一颗基环树,每个节点上有一个点对\((x,y)\),每次询问为给定端点,找一条直线到端点间的所有点的距离之和最小。

设这条直线为\(y=kx+b\),根据点到直线公式得,我们要求\(\sum\limits_{i=1}^n \frac{(kx_i-y_i+b)^2}{k^2+1}\)的最小值,其中\(n\)为路径上的点的个数。

然后开始处理这个式子:

\[\begin{aligned}
&\sum\frac{(kx_i-y_i+b)^2}{k^2+1} \\

=&\sum\frac{(kx_i-y_i)^2+2(kx_i-y_i)b+b^2}{k^2+1} \\

=&\sum\frac{k^2x_i^2-2kx_iy_i+y_i^2+2kbx_i-2by_i+b^2}{k^2+1} \\

=&\frac{\sum k^2x_i^2-\sum 2kx_iy_i+\sum y_i^2+\sum 2kbx_i-\sum 2by_i+nb^2}{k^2+1} \\
\end{aligned}
\]

发现对于直线\(y=kx+b\),\(k\)和\(b\)的取值是互不影响的,所以可以先对\(b\)进行处理,使其取到能让式子达到最小值的值,然后代入原式,再求\(k\)能让式子达到最小值的值。

先对含有\(b\)的项进行分类,得:

\[\frac{nb^2+\sum (2kx_i-2y_i)b+\sum (k^2x_i^2-2kx_iy_i+y_i^2)}{k^2+1}
\]

考虑\(b\)作为变量,发现其为开口向上的二次函数,当\(b= \frac{\sum (2y_i-2kx_i)}{2n}=\frac{\sum y_i}{n}-\frac{\sum kx_i}{n}\),发现\(\frac{\sum x_i}{n}\)的意义就是对于所有\(x_i\)的平均值,所以设\(\bar x=\frac{\sum x_i}{n},\bar y=\frac{\sum y_i}{n}\),将\(b=\bar y-k \bar x\)代入原式,得:

\[\frac{\sum k^2x_i^2-\sum 2kx_iy_i+\sum y_i^2+\sum 2k(\bar y-k \bar x)x_i-\sum 2(\bar y-k \bar x)y_i+n(\bar y-k \bar x)^2}{k^2+1} \\
\]

然后像上面一样,对含有\(k\)的项进行分类,得:

\[\frac{\sum (x_i^2-2x_i \bar x+ \bar x^2)k^2+\sum(-2x_iy_i+2x_i \bar y+2 \bar x y_i -2 \bar x \bar y)k+\sum (y_i^2-2y_i \bar y+ \bar y^2)}{k^2+1}
\]

为方便接下来的处理,设出\(k\)的系数:

\[\begin{aligned}
&A=\sum (x_i^2-2x_i \bar x+ \bar x^2)=\sum x_i^2-\frac{(\sum x_i)^2}{n}\\

&B=\sum(-2x_iy_i+2x_i \bar y+2 \bar x y_i -2 \bar x \bar y)=-2\sum x_iy_i+\frac{2\sum x_i\sum y_i}{n}\\

&C=\sum (y_i^2-2y_i \bar y+ \bar y^2)=\sum y_i^2-\frac{(\sum y_i)^2}{n}\\
\end{aligned}
\]

设原式的值为\(S\),得:

\[\begin{aligned}
&S=\frac{Ak^2+Bk+C}{k^2+1}\\

&Sk^2+S=Ak^2+Bk+C\\

&(A-S)k^2+Bk+C-S=0\\
\end{aligned}
\]

发现得到一个关于\(k\)的一元二次方程,因为\(k\)一定会有合法解,所以得:

\[\begin{aligned}
\Delta&=B^2-4(A-S)(C-S) \geqslant 0\\

&=B^2-4AC+4AS+4CS-4S^2 \geqslant 0\\

&=-4S^2+4(A+C)S+B^2-4AC \geqslant 0\\
\end{aligned}
\]

发现得到一个关于\(S\)的一元二次不等式,将其看作开口向下的二次函数,得其值必须大于等于\(0\),所以\(S\)的最小取值即为其方程的较小根,得:

\[\begin{aligned}
&S=\frac{A+C-\sqrt{A^2+B^2+C^2-2AC}}{2}\\

&A=\sum x_i^2-\frac{(\sum x_i)^2}{n}\\

&B=-2\sum x_iy_i+\frac{2\sum x_i\sum y_i}{n}\\

&C=\sum y_i^2-\frac{(\sum y_i)^2}{n}\\

\end{aligned}
\]

然后维护每个点到根节点\(\sum x_i,\sum y_i,\sum x_i^2,\sum y_i^2,\sum x_iy_i,n\)的和,然后就可以通过树上差分求解了。对于基环树的情况,若两点在一个子树内,就直接树上差分,若两点不在一个子树内,需要经过环,则从经过环的两种方式得出的两个值取较小值作为答案。

\(code:\)

#include<bits/stdc++.h>
#define maxn 400010
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,q,tot;
int de[maxn],f[maxn][25],fa[maxn],a[maxn],b[maxn];
int cir[maxn],bel[maxn],pos[maxn];
bool flag;
bool tag[maxn];
struct edge
{
int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]=(edge){to,head[from]};
head[from]=edge_cnt;
}
struct node
{
int x,y,xd,yd,mul,num;
void insert(int a,int b)
{
x+=a,y+=b,xd+=a*a,yd+=b*b,mul+=a*b,num++;
}
double calc()
{
double A=xd-(double)x*x/num,B=-2*mul+2*(double)x*y/num,C=yd-(double)y*y/num;
return (A+C-sqrt(A*A+B*B+C*C-2*A*C))/2;
}
}s[maxn],sum[maxn];
node operator +(const node &a,const node &b)
{
return (node){a.x+b.x,a.y+b.y,a.xd+b.xd,a.yd+b.yd,a.mul+b.mul,a.num+b.num};
}
node operator -(const node &a,const node &b)
{
return (node){a.x-b.x,a.y-b.y,a.xd-b.xd,a.yd-b.yd,a.mul-b.mul,a.num-b.num};
}
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void dfs(int x,int fath,int col)
{
f[x][0]=fath,de[x]=de[fath]+1,bel[x]=col,s[x]=s[fath],s[x].insert(a[x],b[x]);
for(int i=1;i<=18;++i) f[x][i]=f[f[x][i-1]][i-1];
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fath||tag[y]) continue;
dfs(y,x,col);
}
}
int lca(int x,int y)
{
if(de[x]<de[y]) swap(x,y);
for(int i=18;i>=0;--i)
if(de[f[x][i]]>=de[y])
x=f[x][i];
if(x==y) return x;
for(int i=18;i>=0;--i)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
void dfs_cir(int x,int fath)
{
if(tag[x]&&x!=cir[1])
{
flag=true;
return;
}
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fath) continue;
if(tag[x]&&tag[y]) continue;
cir[++tot]=y,dfs_cir(y,x);
if(flag) return;
tot--;
}
}
double solve(int x,int y)
{
int anc=lca(x,y);
return (s[x]+s[y]-s[anc]-s[f[anc][0]]).calc();
}
int main()
{
read(n),read(m);
for(int i=1;i<=n;++i) read(a[i]),read(b[i]),fa[i]=i;
for(int i=1;i<=m;++i)
{
int x,y;
read(x),read(y),add(x,y),add(y,x);
if(find(x)==find(y)) cir[++tot]=x,tag[x]=tag[y]=true;
else fa[find(x)]=find(y);
}
if(m==n-1) dfs(1,0,0);
else
{
dfs_cir(cir[1],0);
for(int i=1;i<=tot;++i)
{
int x=cir[i];
pos[x]=i,tag[x]=true;
sum[i]=sum[i-1],sum[i].insert(a[x],b[x]);
}
for(int i=1;i<=tot;++i) dfs(cir[i],0,cir[i]);
}
read(q);
while(q--)
{
int x,y;
read(x),read(y);
if(m==n-1) printf("%.6lf\n",solve(x,y));
else
{
if(bel[x]==bel[y]) printf("%.6lf\n",solve(x,y));
else
{
int px=pos[bel[x]],py=pos[bel[y]];
node val=s[x]+s[y]-s[bel[x]]-s[bel[y]];
if(px>py) swap(px,py);
printf("%.6lf\n",min((val+sum[py]-sum[px-1]).calc(),(val+sum[tot]-sum[py-1]+sum[px]).calc()));
}
}
}
return 0;
}

题解 洛谷 P3340 【[ZJOI2014]星系调查】的更多相关文章

  1. bzoj 3528 [ZJOI2014] 星系调查 题解

    [原题] 星系调查 [问题描写叙述] 银河历59451年.在银河系有许很多多已被人类殖民的星系.如果想要在行 星系间往来,大家一般使用连接两个行星系的跳跃星门.  一个跳跃星门能够把 物质在它所连接的 ...

  2. [洛谷P3338] [ZJOI2014]力

    洛谷题目链接:P3338 [ZJOI2014]力 题目描述 给出n个数qi,给出Fj的定义如下: \[F_j = \sum_{i<j}\frac{q_i q_j}{(i-j)^2 }-\sum_ ...

  3. 题解 洛谷P5018【对称二叉树】(noip2018T4)

    \(noip2018\) \(T4\)题解 其实呢,我是觉得这题比\(T3\)水到不知道哪里去了 毕竟我比较菜,不大会\(dp\) 好了开始讲正事 这题其实考察的其实就是选手对D(大)F(法)S(师) ...

  4. 题解 洛谷 P3396 【哈希冲突】(根号分治)

    根号分治 前言 本题是一道讲解根号分治思想的论文题(然鹅我并没有找到论文),正 如论文中所说,根号算法--不仅是分块,根号分治利用的思想和分块像 似却又不同,某一篇洛谷日报中说过,分块算法实质上是一种 ...

  5. 题解-洛谷P5410 【模板】扩展 KMP(Z 函数)

    题面 洛谷P5410 [模板]扩展 KMP(Z 函数) 给定两个字符串 \(a,b\),要求出两个数组:\(b\) 的 \(z\) 函数数组 \(z\).\(b\) 与 \(a\) 的每一个后缀的 L ...

  6. 题解-洛谷P4229 某位歌姬的故事

    题面 洛谷P4229 某位歌姬的故事 \(T\) 组测试数据.有 \(n\) 个音节,每个音节 \(h_i\in[1,A]\),还有 \(m\) 个限制 \((l_i,r_i,g_i)\) 表示 \( ...

  7. 题解-洛谷P4724 【模板】三维凸包

    洛谷P4724 [模板]三维凸包 给出空间中 \(n\) 个点 \(p_i\),求凸包表面积. 数据范围:\(1\le n\le 2000\). 这篇题解因为是世界上最逊的人写的,所以也会有求凸包体积 ...

  8. 题解-洛谷P4859 已经没有什么好害怕的了

    洛谷P4859 已经没有什么好害怕的了 给定 \(n\) 和 \(k\),\(n\) 个糖果能量 \(a_i\) 和 \(n\) 个药片能量 \(b_i\),每个 \(a_i\) 和 \(b_i\) ...

  9. 题解-洛谷P5217 贫穷

    洛谷P5217 贫穷 给定长度为 \(n\) 的初始文本 \(s\),有 \(m\) 个如下操作: \(\texttt{I x c}\),在第 \(x\) 个字母后面插入一个 \(c\). \(\te ...

随机推荐

  1. Spring AOP学习笔记04:AOP核心实现之创建代理

    上文中,我们分析了对所有增强器的获取以及获取匹配的增强器,在本文中我们就来分析一下Spring AOP中另一部分核心逻辑--代理的创建.这部分逻辑的入口是在wrapIfNecessary()方法中紧接 ...

  2. 33_栈程序演示.swf

    pBottom执行栈底有效元素的前一个节点,该节点没有存储有效数据,这样设计是便于栈的管理,向链表一样pHead指向链表的第一个节点,该节点是不存储有效数据的 pTop执行栈顶最新的节点 如果pTop ...

  3. 07.Easymock的实际应用

    第一步下载对应的java包添加到工程中 并静态导入所需要的j类 import static org.easymock.EasyMock.*; 这里有的注意点 package com.fjnu.serv ...

  4. layui动态添加的元素click等事件触发不了的解决办法

    在页面加载完成时候 '.add_project' 元素是可以触发click时间的,当动态添加 '.add_project' 时候,新添加的元素却触发不了click事件,类似下面的写法: $(" ...

  5. 入门大数据---Spark_RDD

    一.RDD简介 RDD 全称为 Resilient Distributed Datasets,是 Spark 最基本的数据抽象,它是只读的.分区记录的集合,支持并行操作,可以由外部数据集或其他 RDD ...

  6. js事件入门(4)

    4.表单事件 表单事件处理主要用来验证表单,可以处理用户在表单上所做的任何操作. 4.1.onsubmit事件 当用户点击submit按钮来提交表单时,就会触发onsubmit事件,如果事件处理程序返 ...

  7. 泊车SLAM文献整理

    1. 泊车: 线车位检测 Geometric Features-Based Parking Slot Detection 译文链接:https://blog.csdn.net/djfjkj52/art ...

  8. Python线程池与进程池

    Python线程池与进程池 前言 前面我们已经将线程并发编程与进程并行编程全部摸了个透,其实我第一次学习他们的时候感觉非常困难甚至是吃力.因为概念实在是太多了,各种锁,数据共享同步,各种方法等等让人十 ...

  9. 「MoreThanJava」Day2:变量、数据类型和运算符

    「MoreThanJava」 宣扬的是 「学习,不止 CODE」,本系列 Java 基础教程是自己在结合各方面的知识之后,对 Java 基础的一个总回顾,旨在 「帮助新朋友快速高质量的学习」. 当然 ...

  10. 使用 PostCSS 进行 CSS 处理

    在 Web 应用开发中,CSS 代码的编写是重要的一部分.CSS 规范从最初的 CSS1 到现在的 CSS3,再到 CSS 规范的下一步版本,规范本身一直在不断的发展演化之中.这给开发人员带来了效率上 ...