点此看题面

大致题意: 给你\(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\)互换的情况。

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

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define RL Reg LL
#define Con const
#define CI Con int&
#define CL Con LL&
#define I inline
#define W while
#define N 1000
#define LL long long
#define DB long double
#define eps 1e-10
#define swap(x,y) (x^=y^=x^=y)
using namespace std;
int n,val[N+5][N+5];Con DB Pi=acos(-1);
Tp I Ty gcd(Con Ty& x,Con Ty& y) {return y?gcd(y,x%y):x;}
struct Fr//存储一个分数
{
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);}//注意约分
I bool operator != (Con Fr& o) Con {return x^o.x||y^o.y;}//不等于
I bool operator < (Con Fr& o) Con {return x^o.x?x<o.x:y<o.y;}//这个小于不是真的小于,只是用于区分不同分数来存储到map中
};
struct Line//存储一条直线,只存斜率和截距
{
Fr Slope,Incre;I Line(Con Fr& s=Fr(),Con Fr& i=Fr()):Slope(s),Incre(i){}
I bool operator < (Con Line& o) Con {return Slope!=o.Slope?Slope<o.Slope:Incre<o.Incre;}//用于区分
};
struct Point//存储一个点
{
#define Dot(A,B) (1LL*(A).x*(B).x+1LL*(A).y*(B).y)//点积
#define Cro(A,B) (1LL*(A).x*(B).y-1LL*(A).y*(B).x)//叉积
#define Len(A) Dot(A,A)//求出长度的平方,之所以不开放是为防炸精度,而且不影响比大小
int x,y;I Point(CI a=0,CI b=0):x(a),y(b){}
I Point operator + (Con Point& o) Con {return Point(x+o.x,y+o.y);}//点相加
I Point operator - (Con Point& o) Con {return Point(x-o.x,y-o.y);}//点相减
}p[N+5];
struct Data//存储极角和对应点编号用于排序
{
int pos;DB ang;I Data(CI p=0,Con DB& a=0):pos(p),ang(a){}
I bool operator < (Con Data& o) Con {return ang<o.ang;}
}s[N<<1];
map<Line,int> pos;vector<int> v[N*N+5];map<LL,int> cnt;
int main()
{
RI i,j,x,y,z,tot,Pc=0,H,T;RL ans=0;Line w;Point t;
for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",&x,&y),p[i]=Point(x,y);
for(i=1;i<=n;++i) for(j=i+1;j<=n;++j)
{
if(p[i].y==p[j].y)//特判y相等
{
w=Line(Fr(1,0),Fr(p[i].x+p[j].x,2)),//求出截距
v[pos.count(w)?pos[w]:pos[w]=++Pc].push_back(p[i].y<<1);//存储中点纵坐标两倍
continue;
}
t=p[j]-p[i],w=Line(Fr(-t.x,t.y),Fr(Dot(t,p[i]+p[j]),2LL*t.y)),//求出斜率和截距
v[pos.count(w)?pos[w]:pos[w]=++Pc].push_back(p[i].x+p[j].x);//存储中点横坐标两倍
}
for(i=1;i<=Pc;++i) sort(v[i].begin(),v[i].end());//排序,用于之后的upper_bound
for(i=1;i<=n;++i)//枚举点D
{
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);//存下极角
for(sort(s+1,s+n),j=1;j^n;++j) (s[j+n-1]=s[j]).ang+=2*Pi;//排序,然后将每个点复制一份,方便后面的双指针
for(cnt.clear(),tot=H=T=0,j=1;j^n;++j)//双指针
{
W(s[T+1].ang<s[j].ang+1.5*Pi-eps) ++T,tot+=cnt[Len(p[s[T].pos]-p[i])]++;//加入新进入半平面的点
W(s[H+1].ang<s[j].ang+0.5*Pi+eps) ++H,tot-=--cnt[Len(p[s[H].pos]-p[i])];//删除新离开半平面的点
val[s[j].pos][i]=tot;//记下结果
}
}
for(i=1;i<=n;++i) for(j=i+1;j<=n;++j)//枚举AD
{
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],//求出当前情况下对应哪种坐标
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))),//求出解析式
#define UB(x) upper_bound(v[z].begin(),v[z].end(),x)
pos.count(w)&&(z=pos[w],ans+=(UB((y<<1)-1)-UB(x<<1))*(val[i][j]+val[j][i]));//如果该解析式存在,更新答案
}return printf("%lld",ans<<2),0;//注意将答案乘4
}

【洛谷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. How to download Heavy Duty Diagnostic Caterpillar SIS 2018 software

    Maybe you find there are supplied Caterpillar SIS 2018 software free download in search engine, that ...

  2. js 递归思想 处理后台多维数组的数据 之 完美契合

    不多BB! 直接看源码 get(tree = []) { let self = this let arr = []; if (!!tree && tree.length !== 0) ...

  3. 设置Yii2发生错误返回json

    如果指示指定一个函数那么可以使用: \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; 如果想整个应用都返回JSO ...

  4. java.lang.IllegalArgumentException: Result Maps collection already contains value for xxx

    本人项目产生此问题的原因是: 本地备份了一份xxxmapper.xml的副本“xxxmapper - 副本.xml”,应该是系统会自动加载“mappe”目录下的所有xml文件. 参考:https:// ...

  5. 基于云计算的IaaS、PaaS、SaaS三种服务模式的区别

    Infrastructure-as-a-Service(IaaS) - 基础即设施服务 基础设施主要包括网络系统(networking).存储设备(storage).服务器(servers).虚拟化技 ...

  6. rancher 2.X 搭建小型web集群+mysql主从复制

    一,环境配置    rancher 2.1.6 二,配置harbor私有仓库 见上文 三,配置私有镜像 01,总文件 dockerfile 为主配置文件,html 为站点文件wordpress.,官网 ...

  7. AUTO Uninstaller【教程】AUTODESK系列软件MAYA,3DSMAX,CAD,INVENTOR,REVIT修复卸载工具 Windows x64位

    小伙伴是不是遇到 MAYA/CAD/3DSMAX/INVENTOR/REVIT 安装失败或者安装不了的问题了呢?AUTODESK系列软件着实令人头疼,MAYA/CAD/3DSMAX/INVENTOR/ ...

  8. php中的$_GET如何获取带有“#”的参数

    <?php echo $_GET['key']; ?> 当url为http://test.com/c.php?key=999时,正常输出:999 当url为http://test.com/ ...

  9. JS字符串与二进制的相互转化

    //字符串转ascii码,用charCodeAt(); //ascii码转字符串,用fromCharCode(); var str = "A"; var code = str.ch ...

  10. 【linux相识相知】sed命令

    在之前的博客中我们介绍了文本三剑客中grep,本次博客就另外一名剑客——sed做出详细的描述,sed真的是一款强大的工具.下面让我们来一起看一下吧! 概述和工作机制 SED的英文全称为Stream E ...