题意

https://loj.ac/problem/2201

思路

说白了就是一条路径上有 \(n\) 个二维坐标,求一条直线使得所有点到此直线的距离和最小。

设这条直线为 \(y=kx+b\) ,距离和为 \(\delta\)。

\[\delta=\sum{(kx_i-y_i+b)^2\over k^2+1}
\]

\[\delta={k^2\sum x_i^2+\sum y_i^2+nb^2-2k\sum x_iy_i+2kb\sum x_i-2b\sum y_i\over k^2+1}
\]

由于 \(k,b\) 的取值互不影响,我们先假设 \(k\) 是一个常量,变形如下

\[\delta={nb^2+(2k\sum x_i-2\sum y_i)b+k^2\sum x_i^2-2k\sum x_iy_i+\sum y_i^2\over k^2+1}
\]

发现就是一个关于 \(b\) 的二次方程,开口朝上,顶点处取最小值,即

\[b=-{2k\sum x_i-2\sum y_i\over 2n}={\sum y_i\over n}-k{\sum x_i\over n}
\]

设 \(\displaystyle\bar x={\sum x_i\over n},\displaystyle\bar y={\sum y_i\over n}\)

得出 \(b=\bar y-k\bar x\)

代入并化简成关于 \(k\) 的式子得到

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

\[\begin{array}{}
A&=\sum x_i^2-2\bar x\sum x_i+n\bar x^2\\
&=\sum x_i^2-{(\sum x_i)^2\over n}\\
B&=-2\sum x_iy_i+2\bar y \sum x_i+2\bar x\sum y_i-2n\bar x\bar y\\
&=-2\sum x_iy_i+2{\sum x_i\sum y_i\over n}\\
C&=\sum y_i^2-2\bar y\sum y_i+n\bar y^2\\
&=\sum y_i^2-{(\sum y_i)^2\over n}
\end{array}
\]

那么

\[\delta={Ak^2+Bk+C\over k^2+1}
\]

把 \(k\) 当作主元化简得

\[(A-\delta)k^2+Bk+C-\delta=0
\]

这个二次方程的 \(\Delta\) 为 \(B^2-4(A-\delta)(C-\delta)\)

\[B^2-4(A-\delta)(C-\delta)\ge 0\\
-4\delta ^2+4(A+C)\delta-4AC+B^2 \ge 0
\]

解得 \(\displaystyle\delta={A+C\pm \sqrt{A^2-2AC+B^2+C^2}\over 2}\)

根号前取负号即可达到最小值.

所以维护六元组 \((\sum x_i,\sum y_i,\sum x_i^2,\sum y_i^2,\sum x_iy_i,\sum1)\) 即可求出 \(A,B,C\) ,求出 \(\delta\) 的最小值。

树的情况只用维护到根路径上的信息即可。

基环树的情况类似,断开一条环边,再枚举可行路径(至多两条)即可。

代码

#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
template<typename T,typename _T>inline bool chk_min(T &x,const _T y){return y<x?x=y,1:0;}
template<typename T,typename _T>inline bool chk_max(T &x,const _T y){return x<y?x=y,1:0;}
typedef long long ll;
const int N=(int)1e5+5;
template<const int maxn,const int maxm,typename T>struct Linked_list
{
int head[maxn],nxt[maxm],tot;T to[maxm];
Linked_list(){clear();}
T &operator [](const int x){return to[x];}
void clear(){memset(head,-1,sizeof(head)),tot=0;}
void add(int u,T v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;}
#define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
struct DisjointSet
{
int fa[N];
void init(int n){FOR(i,1,n)fa[i]=i;}
int get_fa(int x){return x==fa[x]?x:fa[x]=get_fa(fa[x]);}
void merge(int x,int y)
{
x=get_fa(x),y=get_fa(y);
if(x==y)return;
fa[x]=y;
}
};
struct hexa
{
int a,b,c,d,e,f;
hexa(int _a=0,int _b=0,int _c=0,int _d=0,int _e=0,int _f=0)
{
a=_a,b=_b,c=_c,d=_d,e=_e,f=_f;
}
hexa operator +(const hexa &_)const
{
return hexa(a+_.a,b+_.b,c+_.c,d+_.d,e+_.e,f+_.f);
}
hexa operator -(const hexa &_)const
{
return hexa(a-_.a,b-_.b,c-_.c,d-_.d,e-_.e,f-_.f);
}
hexa add(int x,int y)
{
return hexa(a+x,b+y,c+x*x,d+y*y,e+x*y,f+1);
}
double solve()
{
double A=c-1.0*a*a/f,B=-2*e+2.0*a*b/f,C=d-1.0*b*b/f;
return (A+C-sqrt(A*A-2*A*C+B*B+C*C))/2;
}
};
hexa sum[N];
DisjointSet D;
Linked_list<N,N<<1,int>G;
int dep[N],fa[N],sz[N],son[N],top[N];
int X[N],Y[N];
int n,m,q;
int U,V; void dfs(int u,int f,int d)
{
dep[u]=d,fa[u]=f,sz[u]=1,son[u]=0;
sum[u]=sum[f].add(X[u],Y[u]);
EOR(i,G,u)
{
int v=G[i];
if(v==f)continue;
dfs(v,u,d+1);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]])son[u]=v;
}
}
void hld(int u,int f,int tp)
{
top[u]=tp;
if(son[u])hld(son[u],u,tp);
EOR(i,G,u)
{
int v=G[i];
if(v==f||v==son[u])continue;
hld(v,u,v);
}
}
int get_lca(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])std::swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
int get_dis(int x,int y)
{
return dep[x]+dep[y]-2*dep[get_lca(x,y)];
}
hexa get_val(int x,int y)
{
int lca=get_lca(x,y);
return (sum[x]-sum[lca]+sum[y]-sum[lca]).add(X[lca],Y[lca]);
}
bool intersect(int a,int b,int c,int d)
{
int lca1=get_lca(a,b),lca2=get_lca(c,d);
return get_dis(a,lca2)+get_dis(lca2,b)==get_dis(a,b)||
get_dis(c,lca1)+get_dis(lca1,d)==get_dis(c,d);
} int main()
{
scanf("%d%d",&n,&m);
FOR(i,1,n)scanf("%d%d",&X[i],&Y[i]);
D.init(n);
FOR(i,1,m)
{
int u,v;
scanf("%d%d",&u,&v);
if(D.get_fa(u)==D.get_fa(v))U=u,V=v;
else
{
G.add(u,v),G.add(v,u);
D.merge(u,v);
}
}
dfs(1,0,1);
hld(1,0,1);
scanf("%d",&q);
while(q--)
{
int x,y;
scanf("%d%d",&x,&y);
if(n==m-1)printf("%.5lf\n",get_val(x,y).solve());
else
{
double res=get_val(x,y).solve();
if(!intersect(x,U,y,V))chk_min(res,(get_val(x,U)+get_val(y,V)).solve());
if(!intersect(x,V,y,U))chk_min(res,(get_val(x,V)+get_val(y,U)).solve());
printf("%.5lf\n",res);
}
}
return 0;
}

ZJOI 2014 星系调查(推导)的更多相关文章

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

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

  2. 「ZJOI2014」星系调查

    「ZJOI2014」星系调查 本题核心在于快速求XPs 的线性假设相斥度. 点\((x1,y1)\)到直线\(y=kx+b\)的距离的平方为\(\displaystyle {(kx1+b-y1)^2} ...

  3. bzoj 3528: [Zjoi2014]星系调查

    Description 银河历59451年,在银河系有许许多多已被人类殖民的星系.如果想要在行 星系间往来,大家一般使用连接两个行星系的跳跃星门.  一个跳跃星门可以把 物质在它所连接的两个行星系中互 ...

  4. 【BZOJ 3527】【ZJOI 2014】力

    代换一下变成多项式卷积,这里是的答案是两个卷积相减,FFT求一下两个卷积就可以啦 详细的题解:http://www.cnblogs.com/iwtwiioi/p/4126284.html #inclu ...

  5. [ZJOI 2014]力

    Description 给出n个数qi,给出Fj的定义如下: $$F_j = \sum_{i<j}\frac{q_i q_j}{(i-j)^2 }-\sum_{i>j}\frac{q_i ...

  6. 解题:ZJOI 2014 力

    题面 事实说明只会FFT板子是没有用的,还要把式子推成能用FFT/转化一下卷积的方式 虽然这个题不算难的多项式卷积 稍微化简一下可以发现实际是$q_i$和$\frac{1}{(i-j)^2}$在卷,然 ...

  7. BZOJ3528: [Zjoi2014]星系调查

    唉,看到这题直接想起自己的Day1,还是挺难受的,挺傻一题考试的时候怎么就没弄出来呢…… 这两天CP让我给他写个题解,弄了不是很久就把这个题给弄出来了,真不知道考试的时候在干嘛. 明天就出发去北京了, ...

  8. bzoj 3528 [Zjoi2014]星系调查【树链剖分+数学】

    参考:https://www.cnblogs.com/zhuohan123/p/3698852.html 首先,根据点到直线距离公式 \[ d=\frac{kx_0-y_0+b}{\sqrt{k^{2 ...

  9. 【ZJOI 2014】力

    Problem Description 给出 \(n\) 个数 \(q_i\),给出 \(F_j\) 的定义如下: \[F_j=\sum_{i<j} \frac{q_iq_j}{(i-j)^2} ...

随机推荐

  1. 当离开浏览器窗口,提示语title更改

    head里面插入一下代码 <script> document.addEventListener('visibilitychange',function(){if(document.visi ...

  2. vue 基础的一些字眼及路由

    每个框架都有一些自己的写法和一些字眼 摘自 vue 官网 v-bind 缩写 <!-- 完整语法 --> <a v-bind:href="url">...& ...

  3. 如何配置Bitbucket的ssh

    在bitbucket上使用https协议,经常会在提交代码的时候出错,让人很着急上火,但是用ssh就要方便很多.下面介绍一下设置ssh的方法:1.在终端中运行ssh-keygen.2.然后一路ente ...

  4. 【菜鸟学Python】案例一:汇率换算

    汇率换算V1.0 案例描述: 设计一个汇率换算器程序,其功能是将外币换算成人民币,或者相反 案例分析: 分析问题:分析问题的计算部分: 确定问题:将问题划分为输入.处理及输出部分: 设计算法:计算部分 ...

  5. 2018-2019-2 《网络对抗技术》Exp0 Kali安装 Week1 20165321

    安装kali 在vm里面新建虚拟机,选择典型 选择安装程序光盘镜像文件,系统出现无法检测此光盘镜像中的操作系统 虚拟机命名选择安装位置 给虚拟机分配的磁盘大小 点击自定义硬件,更改虚拟机硬件 选择Gr ...

  6. 并发编程实战1-chap1-2-斗者6星

    一.线程安全性 1.线程安全关注点:对共享的和可变的状态的访问 2.JAVA同步机制:synchronized.volatile.显示锁.原子变量 3.无状态对象一定是线程安全的:不包含任何域,也不包 ...

  7. 一张图解释IaaS,PaaS,SaaS

    图片来源于MVA教程:快速入门——面向IT专业人员的Windows Azure IaaS

  8. Docker日志收集最佳实践

    传统日志处理 说到日志,我们以前处理日志的方式如下: · 日志写到本机磁盘上 · 通常仅用于排查线上问题,很少用于数据分析 ·需要时登录到机器上,用grep.awk等工具分析 那么,这种方式有什么缺点 ...

  9. 灵雀云受邀加入VMware 创新网络,共同助力企业数字化进程

        11月15日,在VMware主办的“VMware创新网络”2018高峰论坛上,VMware发布了VMware创新网络(VMwareInnovation Network,VIN)的长期发展规划和 ...

  10. git stash pop 冲突,git stash list 中的记录不会自动删除的解决方法

    在使用git stash代码时,经常会碰到有冲突的情况,一旦出现冲突的话,系统会认为你的stash没有结束. 导致的结果是git stash list 中的列表依然存在,实际上代码已经pop出来了. ...