4814: [Cqoi2017]小Q的草稿

4814: [Cqoi2017]小Q的草稿

Time Limit: 20 Sec Memory Limit: 512 MB
Submit: 266 Solved: 46
[Submit][Status][Discuss]
Description

小Q是个程序员。众所周知,程序员在写程序的时候经常需要草稿纸。小Q现在需要一张草稿纸用来画图,但是桌上
只有一张草稿纸,而且是一张被用过很多次的草稿纸。草稿纸可以看作一个二维平面,小Q甚至已经给它建立了直
角坐标系。以前每一次草稿使用过的区域,都可以近似的看作一个平面上的一个三角形,这个三角形区域的内部和
边界都不能再使用。当然了,以前的草稿也没有出现区域重叠的情况。小Q已经在草稿纸上画上了一些关键点,这
些关键点都在没使用过的区域。小Q想把这些关键点两两之间尽可能的用线段连接起来。连接两个关键点的线段有
可能会穿过已经用过的草稿区域,这样显然不允许。于是小Q就想知道,有多少对关键点可以被线段连接起来,而
且还不会穿过已经用过的区域。为了方便,小Q保证任意三个关键点不会共线。
Input

第一行包含两个整数V,T,表示草稿纸上的关键点数量和三角形区域数量。
接下来V行,每行两个整数x,y,表示一个关键点的坐标(x,y)。
接下来T行,每行六个整数x1,y1,x2,y2,x3,y3,表示一个三角形区域的三个顶点坐标分别是(x1,y1),(x2,y2),(x3,y
3)保证三角形的面积大于0。
V<=1000,T<=1000,0<=所有坐标<=10^8且为整数
Output

输出一行,一个整数,表示能够被线段连接起来的关键点有多少对。
Sample Input

3 0

0 0

2 0

2 2
Sample Output

3

整个草稿纸是全新的,任意两个关键点都可以连线。
HINT

Source

[Submit][Status][Discuss]


显然需要 O(n2) 枚举每个点对,然后判断对答案是否有贡献
问题在于如何快速确定是否被三角形的边阻拦
枚举一个中心点 o ,将所有其他点都对它求一个极角
然后对于平面上的每一个三角形,拆成三条边
显然只需要拿出三条边中对点o限制得最紧的那个
这里的限制是在被覆盖的角度中任取一点,这个位置上这条边到 o 点的距离
这条边显然是两端点对于o张角最大的那个
那么每条边就处理成两个事件,在极角 α 处添加,在极角 β 处删除
对于任意的两条边,肯定是一个时间段一条边对 o 限制得比另一条边更紧些
而且时间段不会分裂成很多块
因为对于边A,如果在某个时刻限制得比 B 更紧了,那么直到它们中某一条被删除,这个情况是持续的
因为任意两个三角形不相交,也就等于任意两条边不相交
于是,对于当前未被删除的边集,显然可以拿出一个set来维护
注意 set 中的比较函数是动态的
总复杂度 O(n2logn)

#include<iostream>  
#include<cstring>  
#include<cstdio>  
#include<cmath>  
#include<vector>
#include<stack>  
#include<algorithm>  
#include<set>  
#define min(a,b) ((a) < (b) ? (a) : (b))  
using namespace std;  

const int maxn = 1010;  
typedef double DB;  
const DB EPS = 1E-10;  
const DB G = 1000;  
typedef long long LL;  

struct Point{  
    DB x,y; Point(){}  
    Point(DB x,DB y): x(x),y(y){}  
    Point operator * (const DB &t) {return Point(x * t,y * t);}  
    Point operator + (const Point &B) {return Point(x + B.x,y + B.y);}  
    Point operator - (const Point &B) {return Point(x - B.x,y - B.y);}  
};  
typedef Point Vector;  

Point o,V[maxn],A[maxn],B[maxn];  

inline DB Dot(Vector v1,Vector v2) {return v1.x * v2.x + v1.y * v2.y;}  
inline DB Length(Vector v) {return sqrt(Dot(v,v));}  
inline DB Cross(Vector v1,Vector v2) {return v1.x * v2.y - v2.x * v1.y;}  

struct Line{  
    Point p; Vector v; Line(){}  
    Line(Point p,Vector v): p(p),v(v){}  
}cur,L[maxn];  

inline Point GetIntersection(Line L1,Line L2)  
{  
    Vector u = L1.p - L2.p;  
    DB t = Cross(L2.v,u) / Cross(L1.v,L2.v);  
    return L1.p + L1.v * t;  
}  

struct Event{  
    Point p; DB ang; int typ,Num; Event(){}  
    Event(Point p,int typ,int Num): p(p),typ(typ),Num(Num){ang = atan2(p.y - o.y,p.x - o.x);}  
    bool operator < (const Event &B) const
    {  
        return (fabs(ang - B.ang) <= EPS) ? typ < B.typ : ang < B.ang;
    }  
}D[maxn * 3];  

int n,m,Ans,I;
bool ins[maxn],vis[maxn];

struct data{  
    int id; data(){}  
    data(int id): id(id){}  
    bool operator < (const data &B) const 
    {  
        Point P1 = GetIntersection(cur,L[id]);
        Point P2 = GetIntersection(cur,L[B.id]);
        return Length(P1 - o) < Length(P2 - o);
    }  
};  

stack <int> stk;
multiset <data> s;  
multiset <data> :: iterator it[maxn];
vector <Point> T[maxn];  

inline DB Calc(Point A,Point B)  
{  
    DB a = Length(A - B),b = Length(A - o),c = Length(B - o);  
    return (b * b + c * c - a * a) / (2.00 * b * c);  
}  

inline bool Check(DB a,DB b,DB c)  
{  
    if (b > c) swap(b,c);  
    return fabs(a - b) <= EPS || fabs(a - c) <= EPS || (b < a && a < c);  
}  

inline bool InLine(Point p,Point p0,Point p1)  
{  
    return Check(p.x,p0.x,p1.x) && Check(p.y,p0.y,p1.y);  
}  

inline void Work(Event e)  
{  
    if (e.typ == 1)
    {
        cur = Line(o,e.p - o); ins[e.Num] = 1;
        Point P1 = GetIntersection(cur,L[4]);
        Point P2 = GetIntersection(cur,L[5]);
        DB d1 = Length(P1 - o),d2 = Length(P2 - o);
        it[e.Num] = s.insert(e.Num);
    } 
    else if (e.typ == 3) s.erase(it[e.Num]),ins[e.Num] = 0;  
    else 
    {  
        if (!s.size()) {++Ans; return;}  
        int now = (*s.begin()).id;  
        Point P = GetIntersection(Line(o,e.p - o),L[now]);  
        if (!InLine(P,A[now],B[now]) || !InLine(P,o,e.p)) ++Ans;
    }  
}  

inline void Solve(int k)  
{  
    int tot = 0; o = V[k];  
    for (int i = 1; i < k; i++)  
        D[++tot] = Event(V[i],2,0);  
    for (int i = 1; i <= m; i++)  
    {  
        pair <DB,int> H[3];  
        for (int j = 0; j < 3; j++)  
            H[j] = make_pair(Calc(T[i][j],T[i][j + 1 == 3 ? 0 : j + 1]),j);  
        sort(H,H + 3); int x = H[0].second,y = x + 1 == 3 ? 0 : x + 1;  
        A[i] = T[i][x]; B[i] = T[i][y]; L[i] = Line(A[i],B[i] - A[i]);  
        if (Cross(A[i] - o,B[i] - o) < 0) swap(A[i],B[i]);  
        D[++tot] = Event(A[i],1,i); D[++tot] = Event(B[i],3,i);  
    }  
    int pos; sort(D + 1,D + tot + 1);  
    for (int i = 1; i <= tot; i++)
        if (D[i].typ == 1) {pos = i; break;}
    for (int i = pos; i <= tot; i++)
        if (D[i].typ == 1) vis[D[i].Num] = 1;
        else if (D[i].typ == 3 && !vis[D[i].Num]) stk.push(D[i].Num);
    for (int i = 1; i < pos; i++)
        if (D[i].typ == 1) vis[D[i].Num] = 1;
        else if (D[i].typ == 3 && !vis[D[i].Num]) stk.push(D[i].Num);
    Work(D[pos]); memset(vis,0,sizeof(vis));
    while (!stk.empty())
    {
        int k = stk.top(); stk.pop();
        it[k] = s.insert(k); ins[k] = 1;
    }
    for (int i = pos + 1; i <= tot; i++)
        Work(D[i]);
    for (int i = 1; i < pos; i++) Work(D[i]);
    for (int i = 1; i <= m; i++)
        if (ins[i]) s.erase(it[i]),ins[i] = 0;
}  

inline int getint()  
{  
    char ch = getchar(); int ret = 0;  
    while (ch < '0' || '9' < ch) ch = getchar();  
    while ('0' <= ch && ch <= '9')  
        ret = ret * 10 + ch - '0',ch = getchar();  
    return ret;  
}  

int main()  
{  
    #ifdef DMC  
        freopen("DMC.txt","r",stdin);  
    #endif  

    n = getint(); m = getint();
    if (!m) {cout << n * (n - 1) / 2 << endl; return 0;}
    for (int i = 1; i <= n; i++)  
    {  
        DB x = getint(),y = getint();  
        V[i] = Point(x / G,y / G);  
    }  
    for (int i = 1; i <= m; i++)  
        for (int j = 0; j < 3; j++)  
        {  
            DB x = getint(),y = getint();  
            T[i].push_back(Point(x / G,y / G));  
        }
    for (I = 2; I <= n; I++) Solve(I);  
    cout << Ans << endl;  
    return 0;  
}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值