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.

 


 

 

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

 

posted @ 2015-02-20 23:04  PlasticSpirit  阅读(346)  评论(0编辑  收藏  举报