首页 > 其他分享 >P4423 题解

P4423 题解

时间:2023-04-28 23:12:21浏览次数:54  
标签:pii return int 题解 mid double P4423 include

前言

题目传送门!

更好的阅读体验?

刚学分治就来写篇题解纪念一下,其实和平面最近点对一样的(总共四倍经验!)。

思路

根据 P7883 的分治思路,这题我们可以考虑用相似的方法解决。

首先将点集按 \(x\) 坐标从小到大排序。然后分治。

对于 \(\left[l, r\right]\) 区间,分治为 \(\left[l, mid\right]\) 与 \(\left[mid+1, r\right]\) 两个区间解决。

容易发现,答案只有以下三种可能:

  1. 由左端三个点构成的三角形。
  2. 由右端三个点构成的三角形。
  3. 左右端加起来取够三个点,构成的三角形。

根据分治的思想,前两类我们已经解决了,于是我们看第三类。

找到 \(x\) 轴意义上中间的那条直线,它就是划分线。

考虑一个常见的性质:对于三角形上的一条边 \(w\),若其周长 \(C \ge k\),则 \(w \le \dfrac{k}{2}\)。原因比较显然就不证了。

应用到这题,设左右两边构成的答案为 \(ans\),则这个点距离中线的距离必须小于 \(\dfrac{ans}{2}\)。

大概就是下面这个样子:

咕咕咕。

代码

额有两个版本,前者是 \(O(n \log^2 n)\) 的递归内 sort() 版本,后者是递归内线性归并的 \(O(n\log n)\) 分治。

写两个主要原因是方便抄袭我想锻炼一下罢了。

\(O(n \log^2 n)\) 的版本:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;

typedef pair <int, int> pii;
#define x first
#define y second
bool cmpx(pii p, pii q) {return p.x < q.x;}
bool cmpy(pii p, pii q) {return p.y < q.y;}

const int N = 2e5 + 5;
pii a[N];
double dis(pii p, pii q) {return sqrt(1ll * (p.x - q.x) * (p.x - q.x) + 1ll * (p.y - q.y) * (p.y - q.y));}
double solve(int l, int r)
{
	if (l >= r) return 1e9;
	int mid = (l + r) >> 1;
	double ans = min(solve(l, mid), solve(mid + 1, r));
	
	vector <pii> tmp;
	for (int i = l; i <= r; i++)
		if (abs(a[i].x - a[mid].x) < ans / 2) //距离中线的距离符合要求
			tmp.push_back(a[i]);
	sort(tmp.begin(), tmp.end(), cmpy);
	
	int siz = tmp.size();
	for (int i = 0; i < siz; i++) //枚举三个符号要求的点
		for (int j = i + 1; j < siz && tmp[j].y - tmp[i].y < ans / 2; j++)
			for (int k = j + 1; k < siz && tmp[k].y - tmp[i].y < ans / 2; k++)
				ans = min(ans, dis(tmp[i], tmp[j]) + dis(tmp[j], tmp[k]) + dis(tmp[k], tmp[i]));
	return ans;
}
int main()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d%d", &a[i].x, &a[i].y);
	sort(a + 1, a + n + 1, cmpx);
	printf("%.6lf", solve(1, n));
	return 0;
}

\(O(n \log n)\) 的版本:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;

typedef pair <int, int> pii;
#define x first
#define y second
bool cmpx(pii p, pii q) {return p.x < q.x;}
bool cmpy(pii p, pii q) {return p.y < q.y;}

const int N = 2e5 + 5;
pii a[N];
double dis(pii p, pii q) {return sqrt(1ll * (p.x - q.x) * (p.x - q.x) + 1ll * (p.y - q.y) * (p.y - q.y));}
pii t[N]; int cur;
void merge(int l, int r) //将 [l,mid] 与 [mid+1,r] 合并,原因是两部分已经是有序的了
{
	int mid = (l + r) >> 1, i = l, j = mid + 1; cur = l;
	while (i <= mid && j <= r)
		if (a[i].y < a[j].y) t[cur++] = a[i++];
		else t[cur++] = a[j++];
	while (i <= mid) t[cur++] = a[i++];
	while (j <= r) t[cur++] = a[j++];
	for (int k = l; k <= r; k++) a[k] = t[k];
}
double solve(int l, int r)
{
	if (l >= r) return 1e9;
	int mid = (l + r) >> 1, midval = a[mid].x; //注意此处一定要先存下来 midval,否则 merge() 会改变
	double ans = min(solve(l, mid), solve(mid + 1, r));
	merge(l, r);
	
	vector <pii> tmp;
	for (int i = l; i <= r; i++)
		if (abs(a[i].x - midval) < ans)
			tmp.push_back(a[i]);
	//sort(tmp.begin(), tmp.end(), cmpy); //这样就不用排序了qwq
	
	int siz = tmp.size();
	for (int i = 0; i < siz; i++) //枚举三个合法点
		for (int j = i + 1; j < siz && tmp[j].y - tmp[i].y < ans / 2; j++)
			for (int k = j + 1; k < siz && tmp[k].y - tmp[i].y < ans / 2; k++)
				ans = min(ans, dis(tmp[i], tmp[j]) + dis(tmp[j], tmp[k]) + dis(tmp[k], tmp[i]));
	return ans;
}
int main()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d%d", &a[i].x, &a[i].y);
	sort(a + 1, a + n + 1, cmpx);
	printf("%.6lf", solve(1, n));
	return 0;
}

希望能帮助到大家!

标签:pii,return,int,题解,mid,double,P4423,include
From: https://www.cnblogs.com/liangbowen/p/17363317.html

相关文章

  • 题解 P3225 [HNOI2012] 矿场搭建
    解析传送门一道简单的tarjan题题意:在无向图中找一些点,这些点组成的的点集记为\(V\),使得去掉任意一个点,剩下的每一个点都可以到达\(V\)中任意一个点,求点集\(V\)的大小的最小值及其方案数。去掉一个点,很自然的联想到割点,那么考虑一下割点在不在备选集合中。如图,显然可以看出,......
  • Codeforces Round 868 (Div. 2) A-E题解
    比赛地址这把真不在状态,麻了,看来还得再练A.A-characteristic题意:给出n和k,要求构造只含1和-1数组a,存在k对(i,j)(i≠j),有a[i]*a[j]=1Solution令构造的数组有x个1和y个-1,那么其对于答案的贡献有\[x*(x-1)/2+y*(y-1)/2\]又因为有x+y=n,所以可以转化成关于x的一元二次方程化简后......
  • COMPSCI 589 问题解答
    COMPSCI589Homework4-Spring2023DueMay6,2023,11:55pmEasternTime1InstructionsThishomeworkassignmentconsistsofaprogrammingportion.Whileyoumaydiscussproblemswithyourpeers,youmustanswerthequestionsonyourownandimplementall......
  • P4681 [THUSC2015]平方运算 题解
    题面链接简要题意给定一个序列,区间.map([](intx){x=x*x%p;});,区间求和。p给定,为小质数。\(N,M\le10^5\)。题解而把一个数看作一个点,向其平方取模连一条边,则最终必然构成一个基环森林,注意到\(P\)很小,每个数经过\(11\)次迭代之后就会进入环中。对于一个区间,如......
  • “makefile:425: *** 遗漏分隔符 。 停止。”问题解决
    在终端下输入make时出现“makefile:2:***遗漏分隔符。停止。”问题,原因是在编写makefile文件时:3:3.c        gcc-o33.cgcc前的是tab分隔符,不能用空格,否则会出现“makefile:2:***遗漏分隔符。停止。”提示。。。make中规定每一Shell命令之前的开头必须使用<t......
  • 题解(开始学知识点
    D.FrogTraveler1900dpgq!https://codeforces.com/contest/1602/problem/D题解:我们可以通过类似bfs的过程找到每个点的能到达的所需步数最小的点,完成更新,但每个点能被哪些点到达很难判断,故我们反过来考虑,如果我们能得到从n->0的最短跳跃次数,亦得解,而每个点下一步能到达的点容......
  • P1345 [USACO5.4]奶牛的电信Telecowmunication 题解
    一、题目描述:n个点,m条边,给定起点s和终点t,求最少删去几个点后,s和t不连通。注意,s和t不能删掉。1<=n<=100,1<=m<=600; 二、解题思路:刚刚学了最大费用流,知道最大流等于最小割。但此题割的不是边,是点。我们需要将将割点转化为割边。把一个点切成两半......
  • 【题解】P3185 [HNOI2007]分裂游戏
    P3185[HNOI2007]分裂游戏题目描述聪聪和睿睿最近迷上了一款叫做分裂的游戏。该游戏的规则是:共有\(n\)个瓶子,标号为\(0,1,\ldots,n-1\),第\(i\)个瓶子中装有\(p_i\)颗巧克力豆,两个人轮流取豆子,每一轮每人选择\(3\)个瓶子,标号为\(i,j,k\),并要保证\(i\ltj,j......
  • 【题解】P4363 [九省联考 2018] 一双木棋 chess
    原题链接题目描述菲菲和牛牛在一块\(n\)行\(m\)列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手。棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束。落子的规则是:一个格子可以落子当且仅当这个格子内没有棋子且这个格子的左侧及上方的所有格子内都有棋......
  • BUAACTF2023 Writeup题解 by Joooook
    BUAACTF2023WriteupbyJoooook目录MiscWhichElementchatgptzhuzhuzhuzhu'srevengeScreenshotcarzymazeMCCryptoBlockCipherMathKeyExchangeWebmotaReverseoneQuiz'srevenge*SnakeMinesweepobfu可以从队名猜一下博主是哪里人(nooffline......