题目链接:点击查看
题目大意:给出n个点,再给出m个询问,每次询问给出一个点 x,我们需要回答包括点 x 的直角三角形有多少个
题目分析:题目比较直接,数据也比较小,支持n*n的算法处理,首先我们必须知道,如果要包括点 x 所组成三角形,那么点 x 可以在直角点上,也可以在非直角点上,比较容易想到的方法是离线处理,我们需要分类讨论,也就是上述两个情况,对于每个询问而言,以点 x 为相对原点,对其余n个点进行极角排序,然后用双指针保证时间复杂度为O(n),一个指针固定住一条直角边,另一个指针去找另一条直角边上有多少个点,这样时间复杂度是O(n*m),解决了点 x 在直角点上的答案,剩下非直角点上的答案我们可以O(n)枚举直角点,同上利用双指针计算贡献,对于有贡献的点 x 累加答案即可,时间复杂度为大概就是n*m*logn的样子,不过实现起来比较麻烦,不想多说
还有一种方法比较简单,但是不太好想,还比较考察代码功底,就是围绕着map展开,首先我们要找直角三角形,本质上是要找垂直的两条边,换句话说就是需要找两个向量垂直,将点抽象成向量就简单多了,和上面大同小异,当点 x 为直角点时,我们记录下点 x 与 n 个点的向量,最后跑一遍map统计答案即可,当点 x 为非直角点时,一样O(n)枚举直角点,O(n)枚举另一个非直角点,然后直接统计答案就好了,实现起来比较简单,不过难点不是main函数里,而是在Point结构体内对小于号的重载,因为我们需要的是向量,也就是说需要一个带斜率的直线,为了方便起见,我们在重载小于号时应该试图将其归类,譬如(2,1)和(4,2)虽然数值不一样,但是代表的都是同一个向量,我们应该将其归为一类而不是两类,第一次知道map竟然可以根据小于号将载入的数据重新分类,有了这样一个方便的性质,在重载好小于号后就可以直接实现main函数里的内容了,比较简单,具体实现看代码
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const int N=2e3+100;
struct Point{
LL x,y;
Point(){}
Point(int _x,int _y){
x = _x;
y = _y;
}
void input(){
scanf("%lld%lld",&x,&y);
}
Point change()const//将所有的向量都转换为非负数
{
if(x<0||x==0&&y<0)
return Point(-x,-y);
return *this;
}
bool operator < (const Point &b)const{
Point t1=change(),t2=b.change();
return t1.x*t2.y<t1.y*t2.x;//利用斜率在map中分类,如果斜率相同则属于同一个向量
}
Point operator -(const Point &b)const{
return Point(x-b.x,y-b.y);
}
}point[N];
map<Point,int>mp;
vector<Point>node;
int ans[N];
int main()
{
// freopen("input.txt","r",stdin);
// ios::sync_with_stdio(false);
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
point[i].input();
for(int i=0;i<m;i++)//查询点为直角点
{
mp.clear();
Point temp;
temp.input();
node.push_back(temp);
for(int i=1;i<=n;i++)
mp[point[i]-temp]++;
for(auto it:mp)
{
Point temp(-it.first.y,it.first.x);
ans[i]+=mp[temp]*it.second;
}
ans[i]/=2;//因为答案累加了两次,需要除以二
}
for(int i=1;i<=n;i++)//枚举n个点分别作为直角点
{
mp.clear();
for(int j=1;j<=n;j++)//枚举另一个点
{
if(i!=j)
mp[point[j]-point[i]]++;
}
for(int j=0;j<m;j++)
{
Point temp=node[j]-point[i];
temp=Point(-temp.y,temp.x);
ans[j]+=mp[temp];
}
}
for(int i=0;i<m;i++)
printf("%d\n",ans[i]);
return 0;
}