题解 洛谷 P3340 【[ZJOI2014]星系调查】
根据题意,发现题目中的图,其实就是一颗树或者是一颗基环树,每个节点上有一个点对\((x,y)\),每次询问为给定端点,找一条直线到端点间的所有点的距离之和最小。
设这条直线为\(y=kx+b\),根据点到直线公式得,我们要求\(\sum\limits_{i=1}^n \frac{(kx_i-y_i+b)^2}{k^2+1}\)的最小值,其中\(n\)为路径上的点的个数。
然后开始处理这个式子:
&\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\)的项进行分类,得:
\]
考虑\(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\)代入原式,得:
\]
然后像上面一样,对含有\(k\)的项进行分类,得:
\]
为方便接下来的处理,设出\(k\)的系数:
&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\),得:
&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\)一定会有合法解,所以得:
\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\)的最小取值即为其方程的较小根,得:
&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]星系调查】的更多相关文章
- bzoj 3528 [ZJOI2014] 星系调查 题解
[原题] 星系调查 [问题描写叙述] 银河历59451年.在银河系有许很多多已被人类殖民的星系.如果想要在行 星系间往来,大家一般使用连接两个行星系的跳跃星门. 一个跳跃星门能够把 物质在它所连接的 ...
- [洛谷P3338] [ZJOI2014]力
洛谷题目链接:P3338 [ZJOI2014]力 题目描述 给出n个数qi,给出Fj的定义如下: \[F_j = \sum_{i<j}\frac{q_i q_j}{(i-j)^2 }-\sum_ ...
- 题解 洛谷P5018【对称二叉树】(noip2018T4)
\(noip2018\) \(T4\)题解 其实呢,我是觉得这题比\(T3\)水到不知道哪里去了 毕竟我比较菜,不大会\(dp\) 好了开始讲正事 这题其实考察的其实就是选手对D(大)F(法)S(师) ...
- 题解 洛谷 P3396 【哈希冲突】(根号分治)
根号分治 前言 本题是一道讲解根号分治思想的论文题(然鹅我并没有找到论文),正 如论文中所说,根号算法--不仅是分块,根号分治利用的思想和分块像 似却又不同,某一篇洛谷日报中说过,分块算法实质上是一种 ...
- 题解-洛谷P5410 【模板】扩展 KMP(Z 函数)
题面 洛谷P5410 [模板]扩展 KMP(Z 函数) 给定两个字符串 \(a,b\),要求出两个数组:\(b\) 的 \(z\) 函数数组 \(z\).\(b\) 与 \(a\) 的每一个后缀的 L ...
- 题解-洛谷P4229 某位歌姬的故事
题面 洛谷P4229 某位歌姬的故事 \(T\) 组测试数据.有 \(n\) 个音节,每个音节 \(h_i\in[1,A]\),还有 \(m\) 个限制 \((l_i,r_i,g_i)\) 表示 \( ...
- 题解-洛谷P4724 【模板】三维凸包
洛谷P4724 [模板]三维凸包 给出空间中 \(n\) 个点 \(p_i\),求凸包表面积. 数据范围:\(1\le n\le 2000\). 这篇题解因为是世界上最逊的人写的,所以也会有求凸包体积 ...
- 题解-洛谷P4859 已经没有什么好害怕的了
洛谷P4859 已经没有什么好害怕的了 给定 \(n\) 和 \(k\),\(n\) 个糖果能量 \(a_i\) 和 \(n\) 个药片能量 \(b_i\),每个 \(a_i\) 和 \(b_i\) ...
- 题解-洛谷P5217 贫穷
洛谷P5217 贫穷 给定长度为 \(n\) 的初始文本 \(s\),有 \(m\) 个如下操作: \(\texttt{I x c}\),在第 \(x\) 个字母后面插入一个 \(c\). \(\te ...
随机推荐
- 【论文笔记】Pyramidal Convolution: Rethinking Convolutional Neural Networks for Visual Recognition
地址:https://arxiv.org/pdf/2006.11538.pdf github:https://github.com/iduta/pyconv 目前的卷积神经网络普遍使用3×3的卷积神经 ...
- Celery浅谈
一.Celery 核心模块 1. Brokers brokers 中文意思为中间人,在这里就是指任务队列本身,接收生产者发来的消息即Task,将任务存入队列.任务的消费者是Worker,Brokers ...
- IDEA2019版中文汉化包
废话不多说,上才艺 E G M~~~~~ 2020版的IDEA大佬可以无视........ 1.打开IDEA文件目录 2.打开lib目录--将汉化版复制到该目录下 3.打开IDEA查看效果 高铁链 ...
- git和github入门指南(5)
5.github上的标签 5.1.标签的作用 给当前版本打一个标签,在github上就会形成一个releases版本 点击进去后,用户就可以下载对应版本的源代码 5.2.在本地git工具上创建标签,同 ...
- 关于for循环和Iterator遍历ArrayList的性能问题
今日看到@DriveMan的一篇博客,题为<ArrayList集合实现RandomAccess接口有何作用?为何LinkedList集合却没实现这接口?>,文中提到对于实现了RandomA ...
- Python之浅谈模块
目录 模块的四种形式 什么是模块 import和from......import 循环导入 模块的搜索路径 Python文件的两种用途 random模块 模块的四种形式 什么是模块 模块就是一个p ...
- 安装pymysql模块及使用
安装pymysql模块: https://www.cnblogs.com/Eva-J/articles/9772614.html file--settings for New Projects---P ...
- rpm部分命令解读
rpm部分命令解读 rpm---RedHat Package Manger---打包及安装工具 rpm参数列表 rpm -a rpm -q < rpm package name> 解读 ...
- 「线段树」「单点修改」洛谷P1198 [JSOI2008]最大数
「线段树」「单点修改」洛谷P1198 [JSOI2008]最大数 题面描述 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数中的最大的数, ...
- Django---进阶2
目录 数据的查,改,删 django orm中如何创建表关系 django请求生命周期流程图(必会) 路由层 路由匹配 无名分组 有名分组 无名有名是否可以混合使用 反向解析 作业 数据的查,改,删 ...