1. 程式人生 > >bzoj1573 [Usaco2009 Open]牛繡花cowemb

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:因為如果兩條和圓相交的直線不存在交點,那麼它們分別相對應的優弧或劣弧總會是相離或包含的,不會是相交 的。

那麼我們就可以想出這樣一個做法:

  1. 我們除去那些和圓不相交的直線,然後把每條直線和圓的兩個交點(若相切,就視為兩個相同的交點)求出 來,並求出兩個交點和圓心連線的極角,標記每個極角所屬的直線編號
  2. 然後把所有的極角降序排序,一個 直線就對應於兩個極角下標所夾的區間,這樣整個圓變成了一個大線段

然後問題變為線段裡有若干個區間,問有多少對區間相交且不包含。
暴力做法很好想。
正解:複雜度為(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
(轉自

http://www.quartergeek.com/bzoj1573/)
其中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;
}