problem

luogu-P3340

题面写得那么长,其实说白了就是求一条直线,使得若干个点到这条直线的距离平方的和最小,求这个最小值。

solution

我超爱数学,数学就是我的命,我一天不学数学我就难受!

假设拟合出的直线为:y=kx+by=kx+by=kx+b。

点到直线 Ax0+By0+C=0Ax_0+By_0+C=0Ax0​+By0​+C=0 的距离公式:d=∣Ax+By+C∣A2+B2d=\frac{|Ax+By+C|}{\sqrt{A^2+B^2}}d=A2+B2​∣Ax+By+C∣​ 不会就去问数学老师

接下来就是推式子:
∑(kxi−yi+b)2k2+1=∑k2xi2+yi2+b2−2kxiyi+2kbxi−2byik2+1\frac{\sum(kx_i-y_i+b)^2}{k^2+1}=\frac{\sum k^2x_i^2+y_i^2+b^2-2kx_iy_i+2kbx_i-2by_i}{k^2+1} k2+1∑(kxi​−yi​+b)2​=k2+1∑k2xi2​+yi2​+b2−2kxi​yi​+2kbxi​−2byi​​
xi,yix_i,y_ixi​,yi​ 都可被视作已知量,所以只有 k,bk,bk,b 是未知数。

答案要最小化这个式子的值,k,bk,bk,b 没有任何等量关系,所以我们两个各自最小化即可。

先最小化 bbb 的求值,把 kkk 假想成已知量。
=∑(b2+(2kxi−2yi)b+yi2−2kxiyi+k2xi2)k2+1=nb2+∑(2kxi−2yi)b+∑(yi2−2kxiyi+k2xi2)k2+1=\frac{\sum(b^2+(2kx_i-2y_i)b+y_i^2-2kx_iy_i+k^2x_i^2)}{k^2+1}=\frac{nb^2+\sum(2kx_i-2y_i)b+\sum(y_i^2-2kx_iy_i+k^2x_i^2)}{k^2+1} =k2+1∑(b2+(2kxi​−2yi​)b+yi2​−2kxi​yi​+k2xi2​)​=k2+1nb2+∑(2kxi​−2yi​)b+∑(yi2​−2kxi​yi​+k2xi2​)​
显然这是个关于 bbb 的二次函数。根据数学知识,我们知道当 b=∑(2yi−2kxi)2nb=\frac{\sum(2y_i-2kx_i)}{2n}b=2n∑(2yi​−2kxi​)​ 时取到最低点。

且 b=∑(2yi−2kxi)2n=∑yi−k∑xinb=\frac{\sum(2y_i-2kx_i)}{2n}=\frac{\sum y_i-k\sum x_i}{n}b=2n∑(2yi​−2kxi​)​=n∑yi​−k∑xi​​,恰好发现 ∑xin=xˉ\frac{\sum x_i}{n}=\bar{x}n∑xi​​=xˉ(平均数),∑yin=yˉ\frac{\sum y_i}{n}=\bar{y}n∑yi​​=yˉ​。

然后现在把 bbb 当作常量 b=yˉ−kxˉb=\bar{y}-k\bar{x}b=yˉ​−kxˉ,把 kkk 当作未知数。
=n(yˉ−kxˉ)2+∑(2kxi−2yi)(yˉ−kxˉ)+∑(yi2−2kxiyi+k2xi2)k2+1=\frac{n(\bar{y}-k\bar{x})^2+\sum(2kx_i-2y_i)(\bar{y}-k\bar{x})+\sum(y_i^2-2kx_iy_i+k^2x_i^2)}{k^2+1} =k2+1n(yˉ​−kxˉ)2+∑(2kxi​−2yi​)(yˉ​−kxˉ)+∑(yi2​−2kxi​yi​+k2xi2​)​

=nyˉ2−2nkxˉyˉ+nk2xˉ2+∑(2kxiyˉ−2yiyˉ−2k2xixˉ+2yikxˉ)+∑(yi2−2kxiyi+k2xi2)k2+1=\frac{n\bar{y}^2-2nk\bar{x}\bar{y}+nk^2\bar{x}^2+\sum(2kx_i\bar{y}-2y_i\bar{y}-2k^2x_i\bar{x}+2y_ik\bar{x})+\sum(y_i^2-2kx_iy_i+k^2x_i^2)}{k^2+1} =k2+1nyˉ​2−2nkxˉyˉ​+nk2xˉ2+∑(2kxi​yˉ​−2yi​yˉ​−2k2xi​xˉ+2yi​kxˉ)+∑(yi2​−2kxi​yi​+k2xi2​)​

=∑(xˉ2−2xixˉ+xi2)k2+∑(−2xˉyˉ+2xiyˉ+2yixˉ−2xiyi)k+∑(yˉ2−2yiyˉ+yi2)k2+1=\frac{\sum(\bar{x}^2-2x_i\bar{x}+x_i^2)k^2+\sum(-2\bar{x}\bar{y}+2x_i\bar{y}+2y_i\bar{x}-2x_iy_i)k+\sum(\bar{y}^2-2y_i\bar{y}+y_i^2)}{k^2+1} =k2+1∑(xˉ2−2xi​xˉ+xi2​)k2+∑(−2xˉyˉ​+2xi​yˉ​+2yi​xˉ−2xi​yi​)k+∑(yˉ​2−2yi​yˉ​+yi2​)​

为了式子的简洁美观,我们记:
{A=∑xˉ2−∑2xix+∑xi2B=−2nxˉyˉ+∑2xiyˉ+∑2yixˉ−∑2xiyiC=nyˉ2−∑2yiyˉ+∑yi2\begin{cases} A=\sum\bar{x}^2-\sum 2x_ix+\sum x_i^2\\ B=-2n\bar{x}\bar{y}+\sum 2x_i\bar{y}+\sum2y_i\bar{x}-\sum2x_iy_i\\ C=n\bar{y}^2-\sum2y_i\bar{y}+\sum y_i^2 \end{cases} ⎩⎪⎨⎪⎧​A=∑xˉ2−∑2xi​x+∑xi2​B=−2nxˉyˉ​+∑2xi​yˉ​+∑2yi​xˉ−∑2xi​yi​C=nyˉ​2−∑2yi​yˉ​+∑yi2​​
则有 Ak2+Bk+Ck2+1→ans\frac{Ak^2+Bk+C}{k^2+1}\rightarrow ansk2+1Ak2+Bk+C​→ans。
ans=Ak2+Bk+Ck2+1⇒ans(k2+1)=Ak2+Bk+C⇒(A−ans)k2+Bk+C−ans=0ans=\frac{Ak^2+Bk+C}{k^2+1}\Rightarrow ans(k^2+1)=Ak^2+Bk+C\Rightarrow (A-ans)k^2+Bk+C-ans=0 ans=k2+1Ak2+Bk+C​⇒ans(k2+1)=Ak2+Bk+C⇒(A−ans)k2+Bk+C−ans=0
因为题目肯定是有解的,所以这个二元一次方程要有根,即 Δ≥0\Delta\ge 0Δ≥0。
KaTeX parse error: Undefined control sequence: \ at position 75: …ns+B^2-4AC\ge 0\̲ ̲
又得到了一个关于 ansansans 的开口向下的二次函数,ansansans 最小取值就是其方程的较小根,再用一次求根公式即可。

所以我们只需要维护 n,∑xi,∑yi,∑xi2,∑yi2,∑xiyin,\sum x_i,\sum y_i,\sum x_i^2,\sum y_i^2,\sum x_iy_in,∑xi​,∑yi​,∑xi2​,∑yi2​,∑xi​yi​ 这几个信息。

一条路径上的信息维护,发现树上差分即可做到,记从根到该点一路上的信息之和。

基环树存一下环的点,然后把环拍成链,考虑有两种走环的方式,比较最小值即可。

code

#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
struct node {int n, sumx, sumy, sumx2, sumy2, sumxy;void insert( int x, int y ) { n ++, sumx += x, sumy += y, sumx2 += x * x, sumy2 += y * y, sumxy += x * y; }double calc() {double var_x = sumx * 1.0 / n, var_y = sumy * 1.0 / n;double A = sumx2 - 2 * var_x * sumx + n * var_x * var_x;double B = - 2 * sumxy + 2 * sumx * var_y + 2 * sumy * var_x - 2 * n * var_x * var_y;double C = sumy2 - 2 * sumy * var_y + n * var_y * var_y;double a = 4, b = - 4 * ( A + C ), c = 4 * A * C - B * B;double delta = sqrt( b * b - 4 * a * c );return ( - b - delta ) / ( 2 * a );}
}f[maxn], g[maxn];
node operator + ( node HM, node NB ) { HM.n += NB.n, HM.sumx += NB.sumx, HM.sumy += NB.sumy, HM.sumx2 += NB.sumx2, HM.sumy2 += NB.sumy2, HM.sumxy += NB.sumxy; return HM; }
node operator - ( node HM, node NB ) { HM.n -= NB.n, HM.sumx -= NB.sumx, HM.sumy -= NB.sumy, HM.sumx2 -= NB.sumx2, HM.sumy2 -= NB.sumy2, HM.sumxy -= NB.sumxy; return HM; }
struct point { int x, y; }p[maxn];
int n, m, Q, cnt;
int fa[maxn], dep[maxn], top[maxn], root[maxn], vis[maxn], siz[maxn], son[maxn], num[maxn], circle[maxn];
vector < int > G[maxn];int lca( int u, int v ) {while( top[u] ^ top[v] )if( dep[top[u]] < dep[top[v]] ) v = fa[top[v]];else u = fa[top[u]];return dep[u] < dep[v] ? u : v;
}void dfs1( int u, int rt ) {root[u] = rt, siz[u] = vis[u] = 1, son[u] = 0;f[u] = f[fa[u]], f[u].insert( p[u].x, p[u].y );for( int v : G[u] ) if( ! vis[v] and v ^ fa[u] ) {fa[v] = u;dep[v] = dep[u] + 1;dfs1( v, rt );siz[u] += siz[v];if( siz[son[u]] < siz[v] ) son[u] = v;}
}void dfs2( int u, int t ) {top[u] = t;if( son[u] ) dfs2( son[u], t );else return;for( int v : G[u] )if( fa[v] == u and son[u] ^ v ) dfs2( v, v );
}void work() {for( int u = 1;u <= n;u ++ )for( int v : G[u] )if( fa[v] ^ u and fa[u] ^ v ) {if( dep[u] > dep[v] ) swap( u, v );while( v ^ u ) {circle[++ cnt] = v;num[v] = cnt;g[cnt] = g[cnt - 1];g[cnt].insert( p[v].x, p[v].y );vis[v] = 1;  v = fa[v];}circle[++ cnt] = u;num[u] = cnt;g[cnt] = g[cnt - 1];g[cnt].insert( p[u].x, p[u].y );vis[u] = 1;return;}
}int main() {scanf( "%d %d", &n, &m );for( int i = 1;i <= n;i ++ ) scanf( "%d %d", &p[i].x, &p[i].y );for( int i = 1, u, v;i <= m;i ++ ) {scanf( "%d %d", &u, &v );G[u].push_back( v );G[v].push_back( u );}dfs1( 1, 1 );memset( vis, 0, sizeof( vis ) );if( n == m ) work();else circle[cnt = 1] = 1;memset( fa, 0, sizeof( fa ) );for( int i = 1;i <= cnt;i ++ ) dfs1( circle[i], circle[i] ), dfs2( circle[i], circle[i] );scanf( "%d", &Q );while( Q -- ) {int x, y;scanf( "%d %d", &x, &y );if( root[x] == root[y] )printf( "%.5f\n", (f[x] + f[y] - f[lca( x, y )] - f[fa[lca( x, y )]] ).calc() );else {if( num[root[x]] > num[root[y]] ) swap( x, y );node u = f[x] - f[root[x]] + f[y] - f[root[y]] + g[num[root[y]]] - g[num[root[x]] - 1];node v = f[x] - f[root[x]] + f[y] - f[root[y]] + g[num[root[x]]] + g[cnt] - g[num[root[y]] - 1];printf( "%.5f\n", min( u.calc(), v.calc() ) );}}return 0;
}

[ZJOI2014] 星系调查(树上差分 + 数学推式子)相关推荐

  1. 牛客月赛60 F.被抓住的小竹(数学推式子)

    牛客月赛60 F.被抓住的小竹(数学&推式子) 考虑枚举每个区间的贡献. 每个区间内所有的数都作为 x x x一次时的贡献和. 因为要求区间内 ≥ x \ge x ≥x数个数, 那么区间内的数 ...

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

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

  3. 差分数组 and 树上差分

    差分数组 定义 百度百科中的差分定义 //其实这完全和要讲的没关系 qwq 进去看了之后是不是觉得看不懂? 那我简单概括一下qwq 差分数组de定义:记录当前位置的数与上一位置的数的差值. 栗子 容易 ...

  4. 天天爱跑步——树上差分

    先来一道简化版: 关联点 2 • 给出一棵二叉树,每个点有点权 ?? • 如果 ? 在 ? 的左(右)子树中,且 ? 到 ? 的距离为 ??,则称 ? 为 ? 的左(右)关联点 • 求每个点的左.右关 ...

  5. 【瞎扯】树上差分的基本思路

    数据结构题中解法千变万化,但分析最近几年的趋势来看,有一种比较重要的思想->树上差分.(会树剖的大神不要嘲笑,虽然很多时候树剖都能很好解决QwQ).至少,树上差分熟练的话还是可以解决很多问题的. ...

  6. 解题报告:AcWing 352. 闇の連鎖(树上差分、方案统计)

    https://www.acwing.com/problem/content/354/ 在没有附加边的情况下,我们发现这是一颗树,那么再添加条附加边(x,y)后,会造成(x,y)之间产生一个环 如果我 ...

  7. 模板 - LCA最近公共祖先(倍增法、Tarjan、树上差分、LCA优化的次小生成树)

    整理的算法模板合集: ACM模板 注意x和y的LCA可以是x或者y本身 一.LCA的在线倍增算法 /*给定一棵包含 n个节点的有根无向树,有 m个询问,每个询问 给出了一对节点的编号 x和 y,询问 ...

  8. [NOIP 2015]运输计划-[树上差分+二分答案]-解题报告

    [NOIP 2015]运输计划 题面: A[NOIP2015 Day2]运输计划 时间限制 : 20000 MS 空间限制 : 262144 KB 问题描述 公元 2044 年,人类进入了宇宙纪元. ...

  9. [Codeforces 555E]Case of Computer Network(Tarjan求边-双连通分量+树上差分)

    [Codeforces 555E]Case of Computer Network(Tarjan求边-双连通分量+树上差分) 题面 给出一个无向图,以及q条有向路径.问是否存在一种给边定向的方案,使得 ...

最新文章

  1. 浅谈Redis和Hbase
  2. MySQL重温笔记-索引
  3. C# 动态加载 动态卸载
  4. C++this指针的用法
  5. 【计算机网络复习 数据链路层】3.4.3 后退N帧协议(GBN)
  6. 未预期的符号 `( 附近有语法错误_沈北附近的换锁上门
  7. android:id=@android:id/list,Logcat错误 - 内容必须有一个ListView的id属性是'android.R.id.list'...
  8. git上传代码前需要检查什么_肝功能检查前需要做什么准备?这6个要点需做好,以免准确度受影响...
  9. Java Filter——敏感词汇过滤
  10. Linux CentOS 7安装Oracle11g超完美教程
  11. 进入地图后分别进行放大缩小操作
  12. 北京大学创业训练营专家讲座:创新大师乔布斯的创业理念与营销哲学
  13. 云计算的三种服务模式:IaaS,PaaS和SaaS
  14. css中标准盒模型和怪异盒模型的区别,如何将标准盒模型转换为怪异盒模型
  15. video视频标签 自动播放autoplay 失效问题
  16. matlab 画思维图像,「4」图像思维
  17. Linux 的父进程和子进程的执行情况(附有案例代码)
  18. 使用cmd(命令提示符)打开文件磁盘或者文件夹
  19. 可重入函数与线程安全的区别与联系
  20. 基于JavaWeb JavaScript的根据时间段的不同,在网页中显示不同的问候语

热门文章

  1. 每日一笑 | 谷歌能严谨到什么地步?
  2. 月薪多少才算80后中的人生赢家?他们的经济、婚姻、生活方式是怎样的
  3. adb android源码分析,Android源码分析(十六)----adb shell 命令进行OTA升级
  4. dubbo优势_Dubbo 迈出云原生重要一步 应用级服务发现解析
  5. php中unset面试题,php unset和引用——由一道php面试题引发的思考
  6. c#web服务器 虚拟目录,C#建立自己的Web服务器
  7. python property setter_Python:动态属性 property setter 以及 __getattr__ 属性
  8. endpointimpl怎么填参数_这是一篇VLOOKUP函数家族主要用法的合集,XLOOKUP来了!真香!但是,没有office365吃不着怎么办?...
  9. 汉诺塔问题详细解析zufeoj
  10. 软件构造学习笔记-第十四周、十五周