根据题意,发现题目中的图,其实就是一颗树或者是一颗基环树,每个节点上有一个点对\((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. 技术干货丨卷积神经网络之LeNet-5迁移实践案例

    摘要:LeNet-5是Yann LeCun在1998年设计的用于手写数字识别的卷积神经网络,当年美国大多数银行就是用它来识别支票上面的手写数字的,它是早期卷积神经网络中最有代表性的实验系统之一.可以说 ...

  2. nginx web服务器概念了解 配置

    服务器 服务器 服务器是一种提供高效计算的机器,与普通的PC主机相比,具有可观的稳定性,高并发性,可扩展性. 互联网任何一个应用都是以服务器为基础设施的,没有服务器我们就无法访问网络上的任何内容,只能 ...

  3. html+css快速入门教程(6)

    9 综合实例 仿百度云盘下载页面实战 10 表格 10.1 table table 为表格标签 可以让我们的表格在浏览器中显示 table 下面还有两个常用标签 tr 和 td tr表示一行 td表示 ...

  4. 文档翻译经验分享(Markdown)

    该教程基于VSCode 加一些插件 youdao translate https://marketplace.visualstudio.com/items?itemName=Yao-Translate ...

  5. Oracle数据库中,误删除或者修改数据恢复方法

    在我们实际工作中,误删除或者修改Oracle数据库中的数据,怎么办呢?这里给大家分享一种解决办法.假如你误操作的时间不超过30分钟(数据库默认的回滚保持段里的数据时间,可以在pl/sql执行窗口按ct ...

  6. Java常见23中设计模式之【代理模式】

    一.静态代理模式 静态代理,使用继承的方式实现自己新增的服务 这种模式可以实现帮助被代理者完成一些前期的准备工作和后期的善后工作,但是核心的业务逻辑仍然是由被代理者完成. 在某些情况下,一个客户不想或 ...

  7. JavaScript图形实例:递归生成树

    观察自然界中树的分叉,一根主干生长出两个侧干,每个侧干又长出两个侧干,以此类推,便生长出疏密有致的结构.这样的生长结构,使用递归算法可以模拟出来. 例如,分叉的侧干按45°的偏转角度进行生长的递归示意 ...

  8. Redis系列(十):数据结构Set源码解析和SADD、SINTER、SDIFF、SUNION、SPOP命令

    1.介绍 Hash是以K->V形式存储,而Set则是K存储,空间节省了很多 Redis中Set是String类型的无序集合:集合成员是唯一的. 这就意味着集合中不能出现重复的数据.可根据应用场景 ...

  9. Windows使用VNC远程连接Linux桌面系统

    sudo yum -y install tigervnc-server  #安装 su - your_user #切换用户 vncpasswd #设置密码 sudo cp /lib/systemd/s ...

  10. 让 JavaScript 与 CSS 和 Sass 对话

    JavaScript 和 CSS 已经并存超过了 20 年.但是在它们之间共享数据非常困难.当然也有大量的尝试.但是我所想到的是一些简单而直观的内容——不涉及结构更改,而是使用 CSS 自定义属性甚至 ...