@description@

给定两个点集 M 与 S,求是否存在一个圆能够分割两个点集。

原题传送门。

@solution@

不妨考虑圆内是 M,圆外是 S 的情况。反过来同理。

如果 M 只有一个点,显然有解;否则至少可以让分割圆经过 M 的凸包上的某两个点。

一个暴力做法是枚举这两个点 x, y,则其他点会对分割圆的位置产生限制。可以用 \(\vec{xy}\) 所对圆周角大小来描述这一限制。

因为 \(\cot\) 在 \((0, \pi)\) 中是单调递减的,且 \(\cot(x) = \cot(x - π)\)。为了方便,我们不妨用 \(\cot(\angle xoy)\) 的大小来描述 \(\vec{xy}\) 所对圆周角。

算 \(\cot\) 时直接用点乘除以叉乘即可。

具体而言,如果 o 是 M 中的点:

当 \(\angle xoy < \pi\) 时正确的圆周角 \(\alpha < \angle xoy\),即 \(\cot(\alpha) > \cot(\angle xoy)\)。

当 \(\angle xoy > \pi\) 时正确的圆周角 \(\alpha > \angle xoy - \pi\),即 \(\cot(\alpha) < \cot(\angle xoy)\)。

S 中的点反过来即可。还要注意判三点共线。

当然暴力肯定过不了。我们不妨再分析一下。

我们可以枚举圆经过的点,然后进行圆反演,之后就是直线分割点集问题。然而这个做法带个 log,过不了。

不妨考虑另一种变换:我们将二维平面的点 (x, y) 变换到三维曲面中的点 (x, y, x^2 + y^2)。

此时联立三维空间中的平面解析式 ax + by + cz = 1 与曲面解析式 z = x^2 + y^2,可以得到 ax + by + c(x^2 + y^2) = 1,也就是平面内圆的解析式。

也就是说,任意平面与这个曲面的交投射在 xy 平面上都是圆。我们就把平面圆分割点集转化成了立体的平面分割点集,平面上方的在圆外,平面下方的在圆内。

。。。看上去好像并没有变简单,我们继续分析。

注意到 M 集合只有上凸包有用,而曲面 z = x^2 + y^2 是下凸的,因此 M 集合中只有平面凸包上点有用(附一张官方题解图)。

可以调整分割平面使得平面经过 M 的立体上凸包的棱。也就是说,我们可以对立体上凸包中每一条棱做一遍最初的暴力。

然而直接求三维凸包依然很头疼。注意到立体上凸包投射到 xy 平面上实际上形成了一个平面凸包的三角剖分,只需要求出这个三角剖分即可。

这个三角剖分有什么性质?每一个三角形对应了立体凸包的一个面,而立体凸包的面的投影是一个圆。

也就是说,每一个三角形的外接圆都是包含整个凸包的极大圆。

我们可以通过分治寻找这个三角剖分,找极大圆的流程一样使用 cot,和暴力检验做法差不多。

由于值域为 C 的整点凸包点数为 \(O(C^{\frac{2}{3}})\)(我也不会证啊),所以求剖分的复杂度为 \(O(n^{\frac{4}{3}})\),求完过后做暴力的总复杂度 \(O(n^{\frac{5}{3}})\)。

@accepted code@

  1. #include <cmath>
  2. #include <cstdio>
  3. #include <algorithm>
  4. using namespace std;
  5. #define double long double
  6. const int MAXN = 10000;
  7. const double INF = 1E18;
  8. const double EPS = 1E-10;
  9. int dcmp(double x) {
  10. return fabs(x) < EPS ? 0 : (x < 0 ? -1 : 1);
  11. }
  12. struct point{
  13. int x, y; point() {}
  14. point(int _x, int _y) : x(_x), y(_y) {}
  15. friend point operator + (point a, point b) {return point(a.x + b.x, a.y + b.y);}
  16. friend point operator - (point a, point b) {return point(a.x - b.x, a.y - b.y);}
  17. friend int operator * (point a, point b) {return a.x*b.x + a.y*b.y;}
  18. friend int operator ^ (point a, point b) {return a.x*b.y - b.x*a.y;}
  19. friend bool operator < (point a, point b) {
  20. return (a.x == b.x ? a.y < b.y : a.x < b.x);
  21. }
  22. };
  23. double cot(point p, point q) {return 1.0 * (p * q) / (p ^ q);}
  24. point a[MAXN + 5], b[MAXN + 5]; int n, m;
  25. bool get(point p1, point p2) {
  26. double l = -INF, r = INF;
  27. for(int i=1;i<=n;i++) {
  28. if( ((p1 - a[i]) ^ (p2 - a[i])) == 0 ) {
  29. if( ((p1 - a[i]) * (p2 - a[i])) > 0 ) return false;
  30. }
  31. else {
  32. double k = cot(p1 - a[i], p2 - a[i]);
  33. if( ((p1 - a[i]) ^ (p2 - a[i])) > 0 )
  34. l = max(l, k);
  35. else r = min(r, k);
  36. }
  37. }
  38. for(int i=1;i<=m;i++) {
  39. if( ((p1 - b[i]) ^ (p2 - b[i])) == 0 ) {
  40. if( ((p1 - b[i]) * (p2 - b[i])) <= 0 ) return false;
  41. }
  42. else {
  43. double k = cot(p1 - b[i], p2 - b[i]);
  44. if( ((p1 - b[i]) ^ (p2 - b[i])) > 0 )
  45. r = min(r, k);
  46. else l = max(l, k);
  47. }
  48. }
  49. return dcmp(r - l) > 0;
  50. }
  51. bool solve(int l, int r) {
  52. if( get(a[l], a[r]) ) return true;
  53. if( l + 1 == r ) return false;
  54. int p = l + 1;
  55. for(int i=l+1;i<=r-1;i++)
  56. if( dcmp(cot(a[r] - a[i], a[l] - a[i]) - cot(a[r] - a[p], a[l] - a[p])) < 0 )
  57. p = i;
  58. return solve(l, p) || solve(p, r);
  59. }
  60. bool check(point *p, int _n, point *q, int _m) {
  61. n = 0; int o = 1;
  62. for(int i=1;i<=_n;i++) {
  63. while( n > o && ((a[n - 1] - a[n]) ^ (p[i] - a[n])) <= 0 )
  64. n--;
  65. a[++n] = p[i];
  66. }
  67. o = n;
  68. for(int i=_n-1;i>=1;i--) {
  69. while( n > o && ((a[n - 1] - a[n]) ^ (p[i] - a[n])) <= 0 )
  70. n--;
  71. a[++n] = p[i];
  72. }
  73. n--;
  74. for(int i=1;i<=_m;i++) b[i] = q[i]; m = _m;
  75. return solve(1, n);
  76. }
  77. point M[MAXN + 5], S[MAXN + 5];
  78. int main() {
  79. int _n, _m; scanf("%d%d", &_n, &_m);
  80. for(int i=1;i<=_n;i++) scanf("%d%d", &M[i].x, &M[i].y);
  81. for(int i=1;i<=_m;i++) scanf("%d%d", &S[i].x, &S[i].y);
  82. sort(M + 1, M + _n + 1), sort(S + 1, S + _m + 1);
  83. puts(_n == 1 || _m == 1 || check(M, _n, S, _m) || check(S, _m, M, _n) ? "YES" : "NO");
  84. }

@details@

集训队作业开门自闭题.jpg。因为种种原因还是决定还是把这道题补了。

调计算几何题调得我想吐,一开始无穷大设小了 WA 了几次,然后 cot 比较写反了又 WA 了几次。顺便去学了个别人的凸包写法。

因为这个凸包不是斜率优化的凸包,所以可以直接叉乘判是否 < 180°。

@codeforces - 549E@ Sasha Circle的更多相关文章

  1. Codeforces 1109D. Sasha and Interesting Fact from Graph Theory

    Codeforces 1109D. Sasha and Interesting Fact from Graph Theory 解题思路: 这题我根本不会做,是周指导带飞我. 首先对于当前已经有 \(m ...

  2. Codeforces 1109D Sasha and Interesting Fact from Graph Theory (看题解) 组合数学

    Sasha and Interesting Fact from Graph Theory n 个 点形成 m 个有标号森林的方案数为 F(n, m) = m * n ^ {n - 1 - m} 然后就 ...

  3. Codeforces 1189B Number Circle

    题目链接:http://codeforces.com/problemset/problem/1189/B AC代码: #include<bits/stdc++.h> using names ...

  4. Codeforces 832A. Sasha and Sticks

    It's one more school day now. Sasha doesn't like classes and is always bored at them. So, each day h ...

  5. Codeforces 1109F - Sasha and Algorithm of Silence's Sounds(LCT)

    Codeforces 题面传送门 & 洛谷题面传送门 讲个笑话,这题是 2020.10.13 dxm 讲题时的一道例题,而我刚好在一年后的今天,也就是 2021.10.13 学 LCT 时做到 ...

  6. CodeForces 718C Sasha and Array

    线段树. 线段树维护区间矩阵和,操作都是最简单的线段树.$lazy$标记不要记录乘了几次,直接记录乘了几次之后的矩阵就可以了,不然每次下传的时候再算一遍时间复杂度会提高. #pragma commen ...

  7. Codeforces 1109E. Sasha and a Very Easy Test 线段树

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF1109E.html 题意 给定一个长度为 n 的数列 a,以及一个模数 M(不一定是质数). 要求支持 q ...

  8. Codeforces 1109D. Sasha and Interesting Fact from Graph Theory 排列组合,Prufer编码

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF1109D.html 题意 所有边权都是 [1,m] 中的整数的所有 n 个点的树中,点 a 到点 b 的距离 ...

  9. [Codeforces Gym] 100162B Circle of Stones

    题意: 桌子上有 n 个石头围成一个环.每个石头都有一种颜色.每种颜色可以由小写英文字母表示.如果每一对相邻的石头都是不同颜色的,则称这 n 个石头构成的环是美丽的.现在,你可以从这 n 个石头中拿走 ...

随机推荐

  1. 三、$JavaScript(1)

    1.闭包 闭包就是能够读取其他函数内部变量的函数 闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以 ...

  2. 【Java】几种典型的内存溢出案例,都在这儿了!

    写在前面 作为程序员,多多少少都会遇到一些内存溢出的场景,如果你还没遇到,说明你工作的年限可能比较短,或者你根本就是个假程序员!哈哈,开个玩笑.今天,我们就以Java代码的方式来列举几个典型的内存溢出 ...

  3. 三,<ul><li>实际应用时遇到的问题

    在布局中使用的比较多的就是这个,快速排列一行或多行文字,还有横排显示作为导航栏标题栏等等书写格式:<ul>    <li>山东教育.....</li></ul ...

  4. Ajax用法查询

    1)$.get(url, [data], [callback], [type]) 2)$.post(url, [data], [callback], [type]) 其中: url:代表请求的服务器端 ...

  5. COLA的扩展性使用和源码研究

    cola扩展点使用和设计初探 封装变化,可灵活应对程序的需求变化. 扩展点使用 步骤: 定义扩展点接口,类型可以是校验器,转换器,实体: 必须以ExtPt结尾,表示一个扩展点. 比如,我定义一个云枢的 ...

  6. [PHP动态]0001.关于 PHP 7 你必须知道的五件事

    1.今年的计划表已出.PHP 7 时间表 RFC 投票一直通过, PHP 7 将在2015年10月发布.尽管有些延迟,但我们还是很高兴它在今年内发布.PHP 7 详细时间表由此查看. 2.PHP 要上 ...

  7. [优文翻译]003.你应避免的移动开发APP的5个细节(5 Things to Avoid while Developing Your Next Mobile App)

    导读:本文是从<5 Things to Avoid while Developing Your Next Mobile App>这篇文章翻译而来 智能手机的普及带动了大批移动应用的诞生,这 ...

  8. 舵机MX-64AR与MX-28AR驱动

    背景:硬件采用485通信,在tb上采购的无需收发控制的串口转RS485模块(485通信为半双工,一般情况需要控制收发模式).在使用该模块后,即可完全使用一个普通地串口来对485通信的舵机进行操作. 模 ...

  9. Rocket - debug - DebugCustomXbar再讨论

    https://mp.weixin.qq.com/s/YPFa6kE6I_Ud_MJGvzmS-g 简单讨论输入边/输出边Bundle的方向. 1. 上游节点的地址不重复 仔细看了一下sourceFn ...

  10. js函数prototype属性学习(二)

    继续探讨js对象的prototype属性,前面已经看到在创建完一个对象之后,随时都会有一个_proto_属性伴随所有,那么,这个_proto_又是用来干嘛的,面试时问的高大上的原型链又是怎么回事? 拿 ...