bzoj1573 [Usaco2009 Open]牛繡花cowemb
Description
Bessie學會了刺繡這種精細的工作。牛們在一片半徑為d(1 <= d <= 50000)的圓形布上繡花. 它們一共繡了N (2 <= N <= 50000)條直線,每條直線連線布的邊緣上的兩個點(沒有兩條線通過邊上同一個點)。 作為一隻熱愛數學的牛,Bessie 知道每條線的公式, ax + by + c = 0. a, b, 和 c 為整數(-1000000 <= a <= 1000000; -1000000 <= b <= 1000000; -1000000 <= c <= 1000000).沒有兩條線完全重合。 不幸的是, 一部分線不通過圓布的內部. 原點(0,0)在布的正中央, 所有邊上的點離原點距離為d. 每條線的公式滿足至少a,b中的一個非零. 對於牛來說,刺繡作品中線的交點越多,便越有價值。幫助Bessie計算在圓中相交的線的對數,也就是說交點與原點的距離小於d。注意如果三條線在圓內同一點相交,這算3對線。4線共點->6對線.
Input
第1行: 兩個空格分開的數, N 和 d 第2..N+1行: 第 i+1 行包含第i條線的引數: a, b 和 c
Output
第1行: 一行,包含一個數,為在園內相交的線的對數.
Sample Input
2 1
1 0 0
0 1 0
輸入說明:
兩條直線x=0和y=0.
Sample Output
1
HINT
兩條線在(0,0)相交, 明顯離原點距離小於1.
Source
Gold
觀察這個題可以發現,兩個直線若存在交點並且交點在圓內,那麼兩個直線所對應於圓上的兩條弧(不管是優弧還是劣弧都是一回事)是 相交且不包含的。
Q:為什麼不用管弧是優弧還是劣弧呢?
A:因為如果兩條和圓相交的直線不存在交點,那麼它們分別相對應的優弧或劣弧總會是相離或包含的,不會是相交 的。
那麼我們就可以想出這樣一個做法:
- 我們除去那些和圓不相交的直線,然後把每條直線和圓的兩個交點(若相切,就視為兩個相同的交點)求出 來,並求出兩個交點和圓心連線的極角,標記每個極角所屬的直線編號
- 然後把所有的極角降序排序,一個 直線就對應於兩個極角下標所夾的區間,這樣整個圓變成了一個大線段
然後問題變為線段裡有若干個區間,問有多少對區間相交且不包含。
暴力做法很好想。
正解:複雜度為(O(nlogn))的做法。
稱某條直線與圓的2個交點中,按照極角排序後較小的為左括號,較大的為右括號。設陣列C[],初始化為0。 for i ∈ 所有交點 if i 為某條直線L的左括號 C[i] = 1 else j = i對應的該條直線的左括號 ans += C[j+1]+…+C[i] C[j] = 0
(轉自
其中C陣列的求和和修改過程如果不優化,那麼這個做法複雜度就是O(n^2)的了,因此用樹狀陣列或線段樹優化這一部分即可。對C陣列求和求的就 是在當前區間的左邊,且和當前區間相交不包含的區間個數。如果訪問到了第i個區間的右端點,那麼之後所有的區間都不會和該區間相交,就要在樹狀數組裡 刪去這個區間的左端點的權值1了。
#include <bits/stdc++.h>
#define N 100010
#define lowbit(x) ((x)&(-(x)))
#define ll long long
using namespace std;
int n;
ll d;
struct Point
{
double ang;
int x;
Point(){}
Point(double _ang,int _bel):ang(_ang),x(_bel){}
}p[N];
bool cmp(Point a,Point b)
{
return a.ang > b.ang;
}
int tot, bit[N], vis[N];
int query(int x)
{
int ans = 0;
while(x)
{
ans += bit[x];
x -= lowbit(x);
}
return ans;
}
void update(int x,int addv)
{
while(x <= 2 * n)
{
bit[x] += addv;
x += lowbit(x);
}
}
int main()
{
ll ans = 0;
scanf("%d%lld",&n,&d);
for(int i = 1; i <= n; i ++)
{
ll a,b,c;
scanf("%lld%lld%lld",&a,&b,&c);
if(c * c <= d * d * (a * a + b * b))
{
if(b != 0)
{
double A = (1 + (double)(a * a) / (double)(b * b));
double B = (double)(2 * a * c) / (double)(b * b);
double C = (double)(c * c) / (double)(b * b) - (double)(d * d);
double x1 = (-B + (double)sqrt(B * B - 4 * A * C)) / (2 * A);
double x2 = (-B - (double)sqrt(B * B - 4 * A * C)) / (2 * A);
double y1 = -((double)a / (double)b) * x1 - (double)c / (double)b;
double y2 = -((double)a / (double)b) * x2 - (double)c / (double)b;
p[++ tot] = Point(atan2(y1,x1),i);
p[++ tot] = Point(atan2(y2,x2),i);
}
else
{
double x = -(double)c / (double)a;
double y1 = sqrt((double)(d * d) - x * x);
double y2 = -sqrt((double)(d * d) - x * x);
p[++ tot] = Point(atan2(y1, x), i);
p[++ tot] = Point(atan2(y2, x), i);
}
}
}
sort(p + 1, p + tot + 1, cmp);
for(int i = 1; i <= tot; i ++)
{
if(vis[p[i].x])
{
ans += query(i) - query(vis[p[i].x]);
update(vis[p[i].x], -1);
}
else
{
vis[p[i].x] = i;
update(i,1);
}
}
printf("%lld\n",ans);
return 0;
}