点此看题面

大致题意: 给你\(n\)个点,让你求鱼形图的数量。

核心思路

首先,考虑到\(n\)这么小,我们可以枚举线段\(AD\),再去找符合条件的\(BC,EF\)。

然后,不难发现\(BC\)与\(EF\)互不影响,因此我们可以分开求对于已知\(AD\)的\(BC\)与\(EF\)的方案数,然后将其相乘。

那么我们现在的问题就在于,如何求出\(BC\)与\(EF\)的方案数了。

\(BC\)的方案数

预处理

考虑到\(AB=AC,BD=CD\),用我这点可怜的初中数学知识,都能证明出\(AD\)垂直平分\(BC\)(貌似作业里还做过这种题目)。

则,我们可以换一个角度,如果枚举\(BC\),那么符合条件的\(AD\)必然满足两个条件:

  • \(\because BC\bot AD,\therefore BC\)与\(AD\)的斜率相乘为\(-1\)。
  • \(\because AD\)平分\(BC,\therefore AD\)必然经过\(BC\)的中点。

而已知斜率和直线上一点,我们就可以推出这条直线的解析式啦!

设\(B(x_B,y_B),C(x_C,y_C)\),则\(BC\)斜率为\(\frac{y_C-y_B}{x_C-x_B}\),\(\therefore AD\)斜率为:

\[-1÷\frac{y_C-y_B}{x_C-x_B}=\frac{x_B-x_C}{y_C-y_B}
\]

又\(\because BC\)中点\(G\)坐标为\((\frac{x_B+x_C}2,\frac{y_B+y_C}2),\therefore AD\)截距为:

\[\frac{x_B-x_C}{y_C-y_B}*\frac{x_B+x_C}2-\frac{y_B+y_C}2=\frac{(x_B-x_C)(x_B+x_C)-(y_C-y_B)(y_B+y_C)}{2(y_C-y_B)}
\]

这样其实我们已经求出了\(AD\)的斜率和截距,但如果你像我一样无聊,可以看一下我对于这里截距的进一步化简。


这个截距的式子显得过于冗长,因此我们可以再转化一下得到:

\[\frac{(x_B-x_C)(x_B+x_C)-(y_C-y_B)(y_B+y_C)}{2(y_C-y_B)}=\frac{(x_B-x_C)(x_B+x_C)+(y_B-y_C)(y_B+y_C)}{2(y_C-y_B)}=\frac {x_{B-C}*x_{B+C}+y_{B-C}*y_{B+C}}{2(y_C-y_B)}
\]

其中,\(B-C,B+C\)皆为向量。

然后,了解一些计算几何公式的人就可以发现,\(x_{B-C}*x_{B+C}+y_{B-C}*y_{B+C}\)其实是一个点积的形式!

于是我们最后得到一个简单的式子:

\[\frac{(B-C)\cdot(B+C)}{2(y_C-y_B)}
\]


而求出了斜率和截距之后,我们就相当于求出了\(AD\)的函数表达式。

则可以考虑把\(AD\)函数表达式相同的\(BC\)的信息全部扔入同一个\(vector\)存储下来,方便后面求答案。

还有要注意的就是对于\(B,C\)两点\(y\)相等的情况要特判,因为此时\(AD\)就不是一次函数了。

求答案

枚举\(AD\)时,我们首先就是求出\(AD\)的函数表达式,然后到对应\(vector\)里去求答案。

此时,我们主要是要满足题目中给出的“\(∠BAD,∠BDA\)与\(∠CAD,∠CDA\)都是锐角”的要求。

也就是说,线段\(AD\)需要与线段\(BC\)有交点。

我们先前已经提过要开\(vector\),但其实不需要直接存下\(BC\),因为这样不太方便。

实际上,我们只要存下中点某一坐标的两倍(两倍是为了可以直接用\(int\)存储),就可以直接在\(vector\)中通过\(upper\_bound\)的方式,分别用\(A,D\)两点的相应坐标查找出两个范围,然后利用前缀和思想来用大的减小的即可得出答案。

至于应该选择哪一坐标,需要根据具体函数解析式来分析了。

如形如\(x=a\)的表达式必须选纵坐标,形如\(y=a\)的表达式必须选横坐标(因为固定的无法判),否则任选。

不过反正我们要特判\(B,C\)纵坐标相等,即\(A,D\)横坐标相等的情况,则我们干脆对于横坐标相等的取纵坐标,不相等的取横坐标,也方便我们判断应用哪个坐标去\(upper\_bound\)。

这一过程有一定细节,具体实现详见代码。

\(EF\)的方案数

对于这个,我们可以考虑,过点\(D\)作\(AD\)的垂线\(l\),则我们将整个平面划分成了两个半平面,而根据题目要求,点\(E,F\)必须在点\(A\)所不在的那个半平面内。

则如果我们枚举点\(D\),然后将其余点按极角排序,我们就可以用双指针,来维护对于每个点\(A\)可能作为点\(E,F\)的所有点。

那么什么情况下两个点才能作为一对点\(E,F\)呢?

正如题目中说的,需要\(DE=DF\)。

因此我们开个\(map\),维护每种到点\(D\)的距离出现的次数,然后就可以用类似于莫队的方式进行维护了,这应该还是比较简单的。

注意,这里我对于相同的两个点只统计了一次,因为最后我将答案乘上了\(4\),即\(BC,EF\)互换的情况。

这一过程同样有一定细节,具体实现详见代码。

代码

  1. #include<bits/stdc++.h>
  2. #define Tp template<typename Ty>
  3. #define Ts template<typename Ty,typename... Ar>
  4. #define Reg register
  5. #define RI Reg int
  6. #define RL Reg LL
  7. #define Con const
  8. #define CI Con int&
  9. #define CL Con LL&
  10. #define I inline
  11. #define W while
  12. #define N 1000
  13. #define LL long long
  14. #define DB long double
  15. #define eps 1e-10
  16. #define swap(x,y) (x^=y^=x^=y)
  17. using namespace std;
  18. int n,val[N+5][N+5];Con DB Pi=acos(-1);
  19. Tp I Ty gcd(Con Ty& x,Con Ty& y) {return y?gcd(y,x%y):x;}
  20. struct Fr//存储一个分数
  21. {
  22. LL x,y;I Fr(CL a=0,CL b=1) {RL g=gcd(a,b);x=a/g,(y=b/g)<0&&(x=-x,y=-y);}//注意约分
  23. I bool operator != (Con Fr& o) Con {return x^o.x||y^o.y;}//不等于
  24. I bool operator < (Con Fr& o) Con {return x^o.x?x<o.x:y<o.y;}//这个小于不是真的小于,只是用于区分不同分数来存储到map中
  25. };
  26. struct Line//存储一条直线,只存斜率和截距
  27. {
  28. Fr Slope,Incre;I Line(Con Fr& s=Fr(),Con Fr& i=Fr()):Slope(s),Incre(i){}
  29. I bool operator < (Con Line& o) Con {return Slope!=o.Slope?Slope<o.Slope:Incre<o.Incre;}//用于区分
  30. };
  31. struct Point//存储一个点
  32. {
  33. #define Dot(A,B) (1LL*(A).x*(B).x+1LL*(A).y*(B).y)//点积
  34. #define Cro(A,B) (1LL*(A).x*(B).y-1LL*(A).y*(B).x)//叉积
  35. #define Len(A) Dot(A,A)//求出长度的平方,之所以不开放是为防炸精度,而且不影响比大小
  36. int x,y;I Point(CI a=0,CI b=0):x(a),y(b){}
  37. I Point operator + (Con Point& o) Con {return Point(x+o.x,y+o.y);}//点相加
  38. I Point operator - (Con Point& o) Con {return Point(x-o.x,y-o.y);}//点相减
  39. }p[N+5];
  40. struct Data//存储极角和对应点编号用于排序
  41. {
  42. int pos;DB ang;I Data(CI p=0,Con DB& a=0):pos(p),ang(a){}
  43. I bool operator < (Con Data& o) Con {return ang<o.ang;}
  44. }s[N<<1];
  45. map<Line,int> pos;vector<int> v[N*N+5];map<LL,int> cnt;
  46. int main()
  47. {
  48. RI i,j,x,y,z,tot,Pc=0,H,T;RL ans=0;Line w;Point t;
  49. for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",&x,&y),p[i]=Point(x,y);
  50. for(i=1;i<=n;++i) for(j=i+1;j<=n;++j)
  51. {
  52. if(p[i].y==p[j].y)//特判y相等
  53. {
  54. w=Line(Fr(1,0),Fr(p[i].x+p[j].x,2)),//求出截距
  55. v[pos.count(w)?pos[w]:pos[w]=++Pc].push_back(p[i].y<<1);//存储中点纵坐标两倍
  56. continue;
  57. }
  58. t=p[j]-p[i],w=Line(Fr(-t.x,t.y),Fr(Dot(t,p[i]+p[j]),2LL*t.y)),//求出斜率和截距
  59. v[pos.count(w)?pos[w]:pos[w]=++Pc].push_back(p[i].x+p[j].x);//存储中点横坐标两倍
  60. }
  61. for(i=1;i<=Pc;++i) sort(v[i].begin(),v[i].end());//排序,用于之后的upper_bound
  62. for(i=1;i<=n;++i)//枚举点D
  63. {
  64. for(tot=0,j=1;j<=n;++j) i^j&&(s[++tot]=Data(j,atan2(p[j].y-p[i].y,p[j].x-p[i].x)),0);//存下极角
  65. for(sort(s+1,s+n),j=1;j^n;++j) (s[j+n-1]=s[j]).ang+=2*Pi;//排序,然后将每个点复制一份,方便后面的双指针
  66. for(cnt.clear(),tot=H=T=0,j=1;j^n;++j)//双指针
  67. {
  68. W(s[T+1].ang<s[j].ang+1.5*Pi-eps) ++T,tot+=cnt[Len(p[s[T].pos]-p[i])]++;//加入新进入半平面的点
  69. W(s[H+1].ang<s[j].ang+0.5*Pi+eps) ++H,tot-=--cnt[Len(p[s[H].pos]-p[i])];//删除新离开半平面的点
  70. val[s[j].pos][i]=tot;//记下结果
  71. }
  72. }
  73. for(i=1;i<=n;++i) for(j=i+1;j<=n;++j)//枚举AD
  74. {
  75. p[i].x^p[j].x?(x=p[i].x,y=p[j].x):(x=p[i].y,y=p[j].y),x>y&&swap(x,y),t=p[i]-p[j],//求出当前情况下对应哪种坐标
  76. w=(p[i].x^p[j].x?Line(Fr(t.y,t.x),Fr(Cro(t,p[i]),t.x)):Line(Fr(1,0),Fr(p[i].x,1))),//求出解析式
  77. #define UB(x) upper_bound(v[z].begin(),v[z].end(),x)
  78. pos.count(w)&&(z=pos[w],ans+=(UB((y<<1)-1)-UB(x<<1))*(val[i][j]+val[j][i]));//如果该解析式存在,更新答案
  79. }return printf("%lld",ans<<2),0;//注意将答案乘4
  80. }

【洛谷5286】[HNOI2019] 鱼(计算几何)的更多相关文章

  1. 洛谷P5292 [HNOI2019]校园旅行(二分图+最短路)

    题面 传送门 题解 如果暴力的话,我们可以把所有的二元组全都扔进一个队列里,然后每次往两边更新同色点,这样的话复杂度是\(O(m^2)\) 怎么优化呢? 对于一个同色联通块,如果它是一个二分图,我们只 ...

  2. 洛谷CF1071E Rain Protection(计算几何,闵可夫斯基和,凸包,二分答案)

    洛谷题目传送门 CF题目传送门 对于这题,我无力吐槽. 虽然式子还是不难想,做法也随便口胡,但是一些鬼畜边界情况就是判不对. 首先显然二分答案. 对于每一个雨滴,它出现的时刻我们的绳子必须落在它上面. ...

  3. 洛谷 P1736 创意吃鱼法

    题目描述 题目链接:https://www.luogu.org/problemnew/show/P1736 回到家中的猫猫把三桶鱼全部转移到了她那长方形大池子中,然后开始思考:到底要以何种方法吃鱼呢( ...

  4. 洛谷P3222 [HNOI2012]射箭(计算几何,半平面交,双端队列)

    洛谷题目传送门 设抛物线方程为\(y=ax^2+bx(a<0,b>0)\),我们想要求出一组\(a,b\)使得它尽可能满足更多的要求.这个显然可以二分答案. 如何check当前的\(mid ...

  5. 洛谷 P2701 [USACO5.3]巨大的牛棚Big Barn Label:二维数组前缀和 你够了 这次我用DP

    题目背景 (USACO 5.3.4) 题目描述 农夫约翰想要在他的正方形农场上建造一座正方形大牛棚.他讨厌在他的农场中砍树,想找一个能够让他在空旷无树的地方修建牛棚的地方.我们假定,他的农场划分成 N ...

  6. 洛谷:P3281 [SCOI2013]数数 (优秀的解法)

    刷了这么久的数位 dp ,照样被这题虐,还从早上虐到晚上,对自己无语...(机房里又是只有我一个人,寂寞.) 题目:洛谷P3281 [SCOI2013]数数 题目描述 Fish 是一条生活在海里的鱼, ...

  7. 洛谷P1387 最大正方形

    题目描述 题目链接:https://www.luogu.org/problemnew/show/P1387 在一个n*m的只包含0和1的矩阵里找出一个不包含0的最大正方形,输出边长. 输入输出格式 输 ...

  8. 洛谷 AT1350 深さ優先探索

    洛谷 AT1350 深さ優先探索 洛谷传送门 题意翻译 高桥先生住的小区是长方形的,被划分成一个个格子.高桥先生想从家里去鱼店,高桥先生每次可以走到他前后左右四个格子中的其中一个,但不能斜着走,也不能 ...

  9. Java实现洛谷 P1428 小鱼比可爱

    题目描述 人比人,气死人:鱼比鱼,难死鱼.小鱼最近参加了一个"比可爱"比赛,比的是每只鱼的可爱程度.参赛的鱼被从左到右排成一排,头都朝向左边,然后每只鱼会得到一个整数数值,表示这只 ...

随机推荐

  1. oracle 基础知识(四) 构成

    一, oracle服务 一个oracle 服务由一个oracle 实例和一个oracle数据库组成. oracle = instance + database 总体概念: 二, oracle 实例 0 ...

  2. CenctOS6 and CenctOS7 多种姿势解决忘记密码

    -----linux---- 忘记密码啦!!! 忘记密码教程!!! 教你们忘记密码(我原来密码就是123456,忘记是不可能的!假装忘记的样子 0.0) 现在我们忘记密码了!对忘记密码了.我忘记密码了 ...

  3. zookeper集群

    1.vi zoo.cfg server.1= ip:2777:3777 server.2= ip:2888:3888 server.3= ip:2999:3999 开启防火墙端口 2.创建myid文件 ...

  4. 踩坑--springboot打war包

    springboot需要jdk1.8+tomcat8.0 第一步:从IDEA中通过maven install将项目打成war包 第二步:将war包放在tomcat的webApp目录下,启动tomcat ...

  5. MySQL查询长数据是无值返回(可以尝试换行符)

    如,要在数据库中包含这样数据的记录有多少条: <table class="link-more-blue" style="width: 100%;" bor ...

  6. python制作 whl 源文件,并制作本地pip源

    制作whl 1.创建用于存放wheel文件目录 mkdir wheels 2.安装wheel库 pip install  wheel 3.进入wheels目录 cd wheels 4.使用pip wh ...

  7. HDU 1800——Flying to the Mars——————【字符串哈希】

    Flying to the Mars Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Other ...

  8. dpkg: error: dpkg status database is locked by another process 解决方法

    使用dpkg -i/apt命令安装,报错: ------------------------------------------------------------- dpkg: error: dpk ...

  9. 深入理解JavaScript系列(3):全面解析Module模式

    简介 Module模式是JavaScript编程中一个非常通用的模式,一般情况下,大家都知道基本用法,本文尝试着给大家更多该模式的高级使用方式. 首先我们来看看Module模式的基本特征: 模块化,可 ...

  10. 简单的Extjs中的Combox选择下拉框使用

    { xtype: "combobox", editable: false, emptyText: "--请选择--", mode: 'local', store ...