Codeforces Round #203 - D. Looking for Owls

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

 


 

 

题意:有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)。

 


 

  1 #include <iostream>

  2 #include <stdio.h>

  3 #include <map>

  4 #include <cmath>

  5 #include <vector>

  6 #include <algorithm>

  7 using namespace std;

  8 #define TR(x) (x+10000)*2

  9 typedef long long ll;

 10 

 11 struct Point

 12 {

 13     int x, y;

 14     Point(int x=0, int y=0):x(x),y(y) { }

 15 };

 16 typedef Point Vector;

 17 

 18 Vector operator - (const Point& A, const Point& B)

 19 {

 20     return Vector(A.x-B.x, A.y-B.y);

 21 }

 22 

 23 struct Circle

 24 {

 25     int x,y,r;

 26 };

 27 

 28 struct Line

 29 {

 30     Point begin;

 31     Point end;

 32 };

 33 

 34 int gcd(int a,int b)

 35 {

 36     return b==0 ? a : gcd(b, a%b);

 37 }

 38 

 39 Vector simpleVector(Vector v)

 40 {

 41     if(v.x==0)

 42         return Vector(0,1);

 43     if(v.y==0)

 44         return Vector(1,0);

 45     int g=gcd(abs(v.x), abs(v.y));

 46     if(v.x>0)

 47         return Vector(v.x/g, v.y/g);

 48     return Vector(-v.x/g, -v.y/g);

 49 }

 50 

 51 Point simpleCenter(Point p, Vector v) // v.x>=0 && p.x>=0 && p.y>=0

 52 {

 53     if(v.x==0)

 54         return Point(p.x,0);

 55     if(v.y==0)

 56         return Point(0,p.y);

 57     Point r;

 58     r.x = p.x % v.x;

 59     int k = (p.x-r.x)/v.x;

 60     r.y=p.y-k*v.y;

 61     return r;

 62 }

 63 

 64 

 65 ll hash(Point p, Vector v)

 66 {

 67     ll ans= (ll)p.x*80001LL

 68             +(ll)p.y*80001LL*80001

 69             +(ll)v.x*80001LL*80001*40007

 70             +(ll)v.y*80001LL*80001*40007*40007;

 71     return ans;

 72 }

 73 

 74 typedef pair<ll, int> Pair;

 75 vector<Pair> a;

 76 

 77 Circle cs[1510];

 78 Line ls[300010];

 79 int main()

 80 {

 81     // 坐标+10000,再放大2倍,坐标范围[0,40000],且都是偶数

 82     freopen("in.txt","r",stdin);

 83     int nLine, nCircle;

 84     cin>> nLine>> nCircle;

 85     for(int i=0; i<nLine; i++)

 86     {

 87         int x1,y1,x2,y2;

 88         scanf("%d %d %d %d",&x1,&y1,&x2,&y2);

 89         ls[i]=Line {Point(TR(x1),TR(y1)),Point(TR(x2),TR(y2))};

 90     }

 91 

 92     for(int i=0; i<nCircle; i++)

 93     {

 94         int x,y,r;

 95         scanf("%d %d %d", &x, &y, &r);

 96         cs[i]=Circle {TR(x),TR(y),r*2};

 97     }

 98 

 99     for(int i=0; i<nCircle; i++)

100         for(int j=i+1; j<nCircle; j++)

101             if(cs[i].r == cs[j].r)

102             {

103                 Vector c1c2=Vector(cs[j].x-cs[i].x, cs[j].y-cs[i].y);

104                 // 判断两个圆是否相交

105                 if(c1c2.x*c1c2.x+c1c2.y*c1c2.y <= 4*cs[i].r*cs[i].r)

106                     continue;

107                 Vector v=Vector(-cs[j].y+cs[i].y, cs[j].x-cs[i].x); // 垂直向量

108                 v=simpleVector(v); // v.x>=0

109 

110                 Point center=Point((cs[j].x+cs[i].x)/2, (cs[j].y+cs[i].y)/2);

111                 center = simpleCenter(center, v);

112 

113                 a.push_back(Pair(hash(center, v), (cs[j].x+cs[i].x)/2));

114             }

115 

116     sort(a.begin(), a.end());

117 

118     int ans=0;

119     for(int i=0; i<nLine; i++)

120     {

121         Vector v=simpleVector(ls[i].begin-ls[i].end);

122         Point p=simpleCenter(ls[i].begin,v);

123         ll h = hash(p,v);

124 

125         // 在vector内二分查找在区间[L,R]内的的个数

126         Pair L = Pair(h, min(ls[i].begin.x, ls[i].end.x));

127         Pair R = Pair(h, max(ls[i].begin.x, ls[i].end.x));

128         vector<Pair>::iterator i1 = lower_bound(a.begin(), a.end(), L);

129         vector<Pair>::iterator i2 = upper_bound(a.begin(), a.end(), R);

130         ans+=i2-i1;

131     }

132 

133     cout<< ans;

134 

135     return 0;

136 }

 

你可能感兴趣的:(codeforces)