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 23 2 3 -20 0 26 0 2

Output

1

Input

3 20 0 0 10 -1 0 10 -1 0 02 0 1-2 0 1

Output

3

Input

1 2-1 0 1 0-100 0 1100 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 }
时间: 2024-11-05 15:53:47

Codeforces Round #203 - D. Looking for Owls的相关文章

Codeforces Round #203 (Div. 2)B Resort

Resort Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Practice CodeForces 350B Description Valera's finally decided to go on holiday! He packed up and headed for a ski resort. Valera's fancied a ski trip b

Codeforces Round #279 (Div. 2) ABCD

Codeforces Round #279 (Div. 2) 做得我都变绿了! Problems # Name     A Team Olympiad standard input/output 1 s, 256 MB  x2377 B Queue standard input/output 2 s, 256 MB  x1250 C Hacking Cypher standard input/output 1 s, 256 MB  x740 D Chocolate standard input/

Codeforces Round #633 (Div. 2)

Codeforces Round #633(Div.2) \(A.Filling\ Diamonds\) 答案就是构成的六边形数量+1 //#pragma GCC optimize("O3") //#pragma comment(linker, "/STACK:1024000000,1024000000") #include<bits/stdc++.h> using namespace std; function<void(void)> __

Educational Codeforces Round 21 G. Anthem of Berland(dp+kmp)

题目链接:Educational Codeforces Round 21 G. Anthem of Berland 题意: 给你两个字符串,第一个字符串包含问号,问号可以变成任意字符串. 问你第一个字符串最多包含多少个第二个字符串. 题解: 考虑dp[i][j],表示当前考虑到第一个串的第i位,已经匹配到第二个字符串的第j位. 这样的话复杂度为26*n*m*O(fail). fail可以用kmp进行预处理,将26个字母全部处理出来,这样复杂度就变成了26*n*m. 状态转移看代码(就是一个kmp

Codeforces Round #428 (Div. 2)

Codeforces Round #428 (Div. 2) A    看懂题目意思就知道做了 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i

Codeforces Round #424 (Div. 2) D. Office Keys(dp)

题目链接:Codeforces Round #424 (Div. 2) D. Office Keys 题意: 在一条轴上有n个人,和m个钥匙,门在s位置. 现在每个人走单位距离需要单位时间. 每个钥匙只能被一个人拿. 求全部的人拿到钥匙并且走到门的最短时间. 题解: 显然没有交叉的情况,因为如果交叉的话可能不是最优解. 然后考虑dp[i][j]表示第i个人拿了第j把钥匙,然后 dp[i][j]=max(val(i,j),min(dp[i-1][i-1~j]))   val(i,j)表示第i个人拿

Codeforces Round #424 (Div. 2) C. Jury Marks(乱搞)

题目链接:Codeforces Round #424 (Div. 2) C. Jury Marks 题意: 给你一个有n个数序列,现在让你确定一个x,使得x通过挨着加这个序列的每一个数能出现所有给出的k个数. 问合法的x有多少个.题目保证这k个数完全不同. 题解: 显然,要将这n个数求一下前缀和,并且排一下序,这样,能出现的数就可以表示为x+a,x+b,x+c了. 这里 x+a,x+b,x+c是递增的.这里我把这个序列叫做A序列 然后对于给出的k个数,我们也排一下序,这里我把它叫做B序列,如果我

Codeforces Round #400 C 前缀和,思维

ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined) C. Molly's Chemicals 题意:n个数,问有多少个区间的和是k的次方数,即sum([l, r])=k^x, x>=0. abs(k)<=10. tags:一开始O(n^2)统计,果然炸了.. 这题要在统计到第 i 个数时,看s[i]-k^x是否在前面出现过.因为k指数增长很快,这样就是O(n). // #400 #include<b

[Codeforces] Round #352 (Div. 2)

人生不止眼前的狗血,还有远方的狗带 A题B题一如既往的丝帛题 A题题意:询问按照12345678910111213...的顺序排列下去第n(n<=10^3)个数是多少 题解:打表,输出 1 #include<bits/stdc++.h> 2 using namespace std; 3 int dig[10],A[1005]; 4 int main(){ 5 int aa=0; 6 for(int i=1;;i++){ 7 int x=i,dd=0; 8 while(x)dig[++dd