@description@

给定 n 个点,第 i 个点位于 (xi, yi)。

在第 i 个点与第 j 个点之间建边费用为 xi*xj + yi*yj。

求最小生成树。

Input

第一行一个整数 T (1≤T≤2000),表示数据组数。

每组数据给定一个整数 n(2≤n≤100000),表示点数。保证 ∑n≤10^6。

接下来 n 行,每行两个整数 xi, yi(1≤xi,yi≤10^6), 描述一个点的坐标。注意可以点可以重合。

Output

对于每组数据,输出最小生成树的权值和。

Sample Input

1

3

2 4

3 1

5 2

Sample Output

27

@solution@

完全图的最小生成树,套路般的 boruvka 算法(可以自己百度一下)。

boruvka 算法的其他部分都是套路,主要是考虑怎么求一个连通块的最小邻边。

注意到点积 \(x_i*x_j + y_i*y_j\) 其实可以写成斜率优化形式的式子 \(y_i*(\frac{x_i}{y_i}*x_j + y_j)\)。当 i 固定时相当于求 \(\frac{x_i}{y_i}*x_j + y_j\) 的最小值。

然后就可以转成维护凸包了。

但是我们还需要排除同一连通块中的点,而众所周知凸包很难实现删除。

可以考虑 cdq 分治(不清楚是不是,不过很像)。将同一连通块的点视作同一颜色,对颜色进行分治。

具体来说,对于区间 [l, r] 中的颜色,我们只考虑 [l, mid] 对 [mid + 1, r] 的贡献与 [mid + 1, r] 对 [l, mid] 的贡献。

分治时分开维护询问与点,通过先递归再归并排序实现横坐标与询问的有序。然后就可以 O(nlogn) 搞定一次查询最小邻边了。

套上生成树的复杂度为 O(nlog^2n)。但由于 boruvka 一般跑不满所以常数小,可以通过。

@accepted code@

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define mp make_pair
#define fi first
#define se second
typedef long long ll;
typedef pair<ll, int> pli;
const int MAXN = 100000;
const ll INF = (1LL<<60);
struct point{
int x, y, c;
point(int _x=0, int _y=0, int _c=0):x(_x), y(_y), c(_c) {}
}pnt[MAXN + 5], qry[MAXN + 5], tmp[MAXN + 5];
double f(point p) {
return - 1.0 * p.x / p.y;
}
bool cmp1(point a, point b) {
return a.c == b.c ? (a.x == b.x ? a.y < b.y : a.x < b.x) : a.c < b.c;
}
bool cmp2(point a, point b) {
return a.c == b.c ? f(a) < f(b) : a.c < b.c;
}
double slope(point a, point b) {
if( a.x == b.x ) {
if( b.y >= a.y ) return INF;
else return -INF;
}
else return 1.0 * (b.y - a.y) / (b.x - a.x);
}
int que[MAXN + 5]; pli lnk[MAXN + 5];
void solve(int l, int r, int L, int R) {
if( L == R ) return ;
int M = (L + R) >> 1, m;
for(int i=l;i<=r;i++)
if( pnt[i].c <= M )
m = i;
solve(l, m, L, M), solve(m + 1, r, M + 1, R); int s = 1, t = 0;
for(int i=l;i<=m;i++) {
while( s < t && slope(pnt[que[t-1]], pnt[que[t]]) >= slope(pnt[que[t]], pnt[i]) )
t--;
que[++t] = i;
}
for(int i=m+1;i<=r;i++) {
while( s < t && slope(pnt[que[s]], pnt[que[s+1]]) <= f(qry[i]) )
s++;
int j = que[s];
lnk[qry[i].c] = min(lnk[qry[i].c], mp(1LL*pnt[j].x*qry[i].x + 1LL*pnt[j].y*qry[i].y, pnt[j].c));
}
s = 1, t = 0;
for(int i=m+1;i<=r;i++) {
while( s < t && slope(pnt[que[t-1]], pnt[que[t]]) >= slope(pnt[que[t]], pnt[i]) )
t--;
que[++t] = i;
}
for(int i=l;i<=m;i++) {
while( s < t && slope(pnt[que[s]], pnt[que[s+1]]) <= f(qry[i]) )
s++;
int j = que[s];
lnk[qry[i].c] = min(lnk[qry[i].c], mp(1LL*pnt[j].x*qry[i].x + 1LL*pnt[j].y*qry[i].y, pnt[j].c));
}
int p = l, q = m + 1, k = l;
while( p <= m && q <= r ) {
if( pnt[p].x < pnt[q].x )
tmp[k++] = pnt[p++];
else tmp[k++] = pnt[q++];
}
while( p <= m ) tmp[k++] = pnt[p++];
while( q <= r ) tmp[k++] = pnt[q++];
for(int i=l;i<=r;i++) pnt[i] = tmp[i];
p = l, q = m + 1, k = l;
while( p <= m && q <= r ) {
if( f(qry[p]) < f(qry[q]) )
tmp[k++] = qry[p++];
else tmp[k++] = qry[q++];
}
while( p <= m ) tmp[k++] = qry[p++];
while( q <= r ) tmp[k++] = qry[q++];
for(int i=l;i<=r;i++) qry[i] = tmp[i];
}
int id[MAXN + 5], fa[MAXN + 5], clr[MAXN + 5];
int find(int x) {
return fa[x] = (fa[x] == x ? x : find(fa[x]));
}
bool unite(int x, int y) {
int fx = find(x), fy = find(y);
if( fx != fy ) {
fa[fx] = fy;
return true;
}
else return false;
}
int x[MAXN + 5], y[MAXN + 5], n;
void solve() {
scanf("%d", &n);
for(int i=1;i<=n;i++)
scanf("%d%d", &x[i], &y[i]), fa[i] = i;
ll ans = 0;
while( true ) {
int cnt = 0;
for(int i=1;i<=n;i++)
if( fa[i] == i )
id[clr[i] = (++cnt)] = i;
if( cnt == 1 ) break;
for(int i=1;i<=n;i++)
clr[i] = clr[find(i)];
for(int i=1;i<=n;i++)
pnt[i] = qry[i] = point(x[i], y[i], clr[i]);
sort(pnt + 1, pnt + n + 1, cmp1);
sort(qry + 1, qry + n + 1, cmp2);
for(int i=1;i<=cnt;i++)
lnk[i] = mp(INF, -1);
solve(1, n, 1, cnt);
for(int i=1;i<=cnt;i++)
if( unite(id[i], id[lnk[i].se]) )
ans += lnk[i].fi;
}
printf("%lld\n", ans);
}
int main() {
int T; scanf("%d", &T);
while( T-- ) solve();
}

@details@

其实。。。我一直卡在把询问和横坐标分开排序这一点上。。。

一直在想怎么才能避免在凸包上二分。。。

@hdu - 6329@ Problem K. Transport Construction的更多相关文章

  1. HDU 6342.Problem K. Expression in Memories-模拟-巴科斯范式填充 (2018 Multi-University Training Contest 4 1011)

    6342.Problem K. Expression in Memories 这个题就是把?变成其他的使得多项式成立并且没有前导零 官方题解: 没意思,好想咸鱼,直接贴一篇别人的博客,写的很好,比我的 ...

  2. 2018 Multi-University Training Contest 4 Problem K. Expression in Memories 【模拟】

    任意门:http://acm.hdu.edu.cn/showproblem.php?pid=6342 Problem K. Expression in Memories Time Limit: 200 ...

  3. Codeforces Gym 100610 Problem K. Kitchen Robot 状压DP

    Problem K. Kitchen Robot Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/10061 ...

  4. Codeforces 1089K - King Kog's Reception - [线段树][2018-2019 ICPC, NEERC, Northern Eurasia Finals Problem K]

    题目链接:https://codeforces.com/contest/1089/problem/K time limit per test: 2 seconds memory limit per t ...

  5. Gym 101981K - Kangaroo Puzzle - [玄学][2018-2019 ACM-ICPC Asia Nanjing Regional Contest Problem K]

    题目链接:http://codeforces.com/gym/101981/problem/K Your friend has made a computer video game called “K ...

  6. Western Subregional of NEERC, Minsk, Wednesday, November 4, 2015 Problem K. UTF-8 Decoder 模拟题

    Problem K. UTF-8 Decoder 题目连接: http://opentrains.snarknews.info/~ejudge/team.cgi?SID=c75360ed7f2c702 ...

  7. 2010-2011 ACM-ICPC, NEERC, Moscow Subregional Contest Problem K. KMC Attacks 交互题 暴力

    Problem K. KMC Attacks 题目连接: http://codeforces.com/gym/100714 Description Warrant VI is a remote pla ...

  8. XVII Open Cup named after E.V. Pankratiev Grand Prix of Moscow Workshops, Sunday, April 23, 2017 Problem K. Piecemaking

    题目:Problem K. PiecemakingInput file: standard inputOutput file: standard outputTime limit: 1 secondM ...

  9. HDU 6343.Problem L. Graph Theory Homework-数学 (2018 Multi-University Training Contest 4 1012)

    6343.Problem L. Graph Theory Homework 官方题解: 一篇写的很好的博客: HDU 6343 - Problem L. Graph Theory Homework - ...

随机推荐

  1. fore end common url

    1.Fore end course 1)less http://www.bootcss.com/p/lesscss/2.Fore end official website 1)W3C(HTML/CSS ...

  2. java并发系列(一)-----多线程简介、创建以及生命周期

    进程.线程与任务 进程:程序的运行实例.打开电脑的任务管理器,如下: 正在运行的360浏览器就是一个进程.运行一个java程序的实质是启动一个java虚拟机进程,也就是说一个运行的java程序就是一个 ...

  3. 使用Jedis操作Redis-使用Java语言在客户端操作---对key的操作

    //添加String类型的模拟数据. jedis.set("mykey", "2"); jedis.set("mykey2", " ...

  4. Python3数据分析与挖掘建模实战

    Python3数据分析与挖掘建模实战  整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问题,大家看的时 ...

  5. java图形验证码生成工具类及web页面校验验证码

    最近做验证码,参考网上案例,发现有不少问题,特意进行了修改和完善. 验证码生成器: import javax.imageio.ImageIO; import java.awt.*; import ja ...

  6. 模板与泛型编程 c++ primer ch16.1

    在摸板定义中,模板参数列表不能为空, 编译器用推断出的参数来进行 实例化(instantiation) 一般来说 模板是有type parameter 但是也可以声明 nontype paramete ...

  7. 2018-2-13-wpf-如何使用-Magick.NET-播放-gif-图片

    title author date CreateTime categories wpf 如何使用 Magick.NET 播放 gif 图片 lindexi 2018-2-13 17:23:3 +080 ...

  8. 【CodeVS】1023 GPA计算

    1023 GPA计算 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 青铜 Bronze 题目描述 Description 小松终于步入了大学的殿堂,带着兴奋和憧憬,他参加了信息科学 ...

  9. PAI-STUDIO通过Tensorflow处理MaxCompute表数据

    PAI-STUDIO在支持OSS数据源的基础上,增加了对MaxCompute表的数据支持.用户可以直接使用PAI-STUDIO的Tensorflow组件读写MaxCompute数据,本教程将提供完整数 ...

  10. qq在线

    <a target="_blank" href="http://wpa.qq.com/msgrd?v=3&uin=1749904992&site=q ...