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
#include
#include
#include
#include
#include
#include
#include
#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 s;
multiset :: iterator it[maxn];
vector 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 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;
}