Codeforces Round #203 - D. Looking for Owls

时间:2021-10-15 21:34:39
D. Looking for Owls

Emperor Palpatine loves owls very much. The emperor has some blueprints with the new Death Star, the blueprints contain n distinct segments and m distinct circles. We will consider the segments indexed from 1 to n in some way and the circles — indexed from 1 to m in some way.

Palpatine defines an owl as a set of a pair of distinct circles (i, j) (i < j) and one segment k, such that:

  1. circles i and j are symmetrical relatively to the straight line containing segment k;
  2. circles i and j don't have any common points;
  3. circles i and j have the same radius;
  4. segment k intersects the segment that connects the centers of circles i and j.

Help Palpatine, count the number of distinct owls on the picture.

Input

The first line contains two integers — n and m (1 ≤ n ≤ 3·105, 2 ≤ m ≤ 1500).

The next n lines contain four integers each, x1, y1, x2, y2 — the coordinates of the two endpoints of the segment. It's guaranteed that each segment has positive length.

The next m lines contain three integers each, xi, yi, ri — the coordinates of the center and the radius of the i-th circle. All coordinates are integers of at most 104 in their absolute value. The radius is a positive integer of at most 104.

It is guaranteed that all segments and all circles are dictinct.

Output

Print a single number — the answer to the problem.

Please, do not use the %lld specifier to output 64-bit integers is С++. It is preferred to use the cout stream or the %I64d specifier.

Sample test(s)
Input
1 2
3 2 3 -2
0 0 2
6 0 2
Output
1
Input
3 2
0 0 0 1
0 -1 0 1
0 -1 0 0
2 0 1
-2 0 1
Output
3
Input
1 2
-1 0 1 0
-100 0 1
100 0 1
Output
0
Note

Here's an owl from the first sample. The owl is sitting and waiting for you to count it.

Codeforces Round #203 - D. Looking for Owls

aaarticlea/png;base64,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" alt="" />


题意:有n条线段和m个圆(1 ≤ n ≤ 3·105, 2 ≤ m ≤ 1500),如果存在两个圆和这两个圆的对称线段(并且线段要和圆心的连线相交),那么一起称为一个owl,求一共有多少个owl。

思路:最暴力的做法就是枚举每一对圆,求它们的对称线,然后枚举每一条线段,判断线段是否在对称线上,复杂度o(m2n),但是数据规模太大无法承受,这题不是一道纯粹的几何题。

后来我想,如果在暴力的基础上改进一下,先预处理出每对圆的对称线,在枚举每一条线段的时候,如果能在o(1)或o(logn)内判断出这条对称线是否存在,应该能过。

于是我想到了将一条直线hash成一个long long值,这种hash还是第一次写,我将这条直线对应的向量通过求gcd将x,y都缩到最小,并且保证x非负,然后在这条直线上取一点使得x非负并且尽量小,将这向量和这点的坐标值随便乘一些很大的数再加起来就算hash了,这样就可以保证同一条直线不管已知哪两点,算出的hash值都唯一。

现在问题来了,怎么判断线段和圆心连线相交呢?如果再找出这两个圆心的话,那跟暴力无异。于是我又想到了将一个对称线的hash值和对称线和圆心的交点的x坐标弄成一个pair插入到一个vector中,全部完了后对vector排序,hash值小的在前,相等的话x坐标小的在前面,然后枚举每一条线段,将两端点和对应的向量也都hash一下然后弄成pair,对应用lower_bound和upper_bound二分查找,在这个区间内的pair肯定都是符合的,ans+=区间长度就可以了。

于是时间复杂度降到了o(m2+nlogm2)。


 #include <iostream>
#include <stdio.h>
#include <map>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
#define TR(x) (x+10000)*2
typedef long long ll; struct Point
{
int x, y;
Point(int x=, int y=):x(x),y(y) { }
};
typedef Point Vector; Vector operator - (const Point& A, const Point& B)
{
return Vector(A.x-B.x, A.y-B.y);
} struct Circle
{
int x,y,r;
}; struct Line
{
Point begin;
Point end;
}; int gcd(int a,int b)
{
return b== ? a : gcd(b, a%b);
} Vector simpleVector(Vector v)
{
if(v.x==)
return Vector(,);
if(v.y==)
return Vector(,);
int g=gcd(abs(v.x), abs(v.y));
if(v.x>)
return Vector(v.x/g, v.y/g);
return Vector(-v.x/g, -v.y/g);
} Point simpleCenter(Point p, Vector v) // v.x>=0 && p.x>=0 && p.y>=0
{
if(v.x==)
return Point(p.x,);
if(v.y==)
return Point(,p.y);
Point r;
r.x = p.x % v.x;
int k = (p.x-r.x)/v.x;
r.y=p.y-k*v.y;
return r;
} ll hash(Point p, Vector v)
{
ll ans= (ll)p.x*80001LL
+(ll)p.y*80001LL*
+(ll)v.x*80001LL**
+(ll)v.y*80001LL***;
return ans;
} typedef pair<ll, int> Pair;
vector<Pair> a; Circle cs[];
Line ls[];
int main()
{
// 坐标+10000,再放大2倍,坐标范围[0,40000],且都是偶数
freopen("in.txt","r",stdin);
int nLine, nCircle;
cin>> nLine>> nCircle;
for(int i=; i<nLine; i++)
{
int x1,y1,x2,y2;
scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
ls[i]=Line {Point(TR(x1),TR(y1)),Point(TR(x2),TR(y2))};
} for(int i=; i<nCircle; i++)
{
int x,y,r;
scanf("%d %d %d", &x, &y, &r);
cs[i]=Circle {TR(x),TR(y),r*};
} for(int i=; i<nCircle; i++)
for(int j=i+; j<nCircle; j++)
if(cs[i].r == cs[j].r)
{
Vector c1c2=Vector(cs[j].x-cs[i].x, cs[j].y-cs[i].y);
// 判断两个圆是否相交
if(c1c2.x*c1c2.x+c1c2.y*c1c2.y <= *cs[i].r*cs[i].r)
continue;
Vector v=Vector(-cs[j].y+cs[i].y, cs[j].x-cs[i].x); // 垂直向量
v=simpleVector(v); // v.x>=0 Point center=Point((cs[j].x+cs[i].x)/, (cs[j].y+cs[i].y)/);
center = simpleCenter(center, v); a.push_back(Pair(hash(center, v), (cs[j].x+cs[i].x)/));
} sort(a.begin(), a.end()); int ans=;
for(int i=; i<nLine; i++)
{
Vector v=simpleVector(ls[i].begin-ls[i].end);
Point p=simpleCenter(ls[i].begin,v);
ll h = hash(p,v); // 在vector内二分查找在区间[L,R]内的的个数
Pair L = Pair(h, min(ls[i].begin.x, ls[i].end.x));
Pair R = Pair(h, max(ls[i].begin.x, ls[i].end.x));
vector<Pair>::iterator i1 = lower_bound(a.begin(), a.end(), L);
vector<Pair>::iterator i2 = upper_bound(a.begin(), a.end(), R);
ans+=i2-i1;
} cout<< ans; return ;
}