凸包内点的数量 J-Saint John Festival @ 2015-2016 ACM-ICPC Southwestern Europe Regional Contest (SWERC 15)
题意
给你n个红点,m个黑点,每个包含在红点三角形中的黑点对答案有一个贡献(同黑点被多个三角形包含只算一次)。
2015-2016 ACM-ICPC Southwestern Europe Regional Contest (SWERC 15)
银~金牌题??
分析与实现
黑点被某红点三角形包含等价于黑点被所有红点组成的凸包包含。
所以直接做个凸包然后用点在凸包内板子 O ( m l o g n ) O(mlogn) O(mlogn)即可。
然后板子用炸了。
坑点
板子规则:
没用过的板子假设它是错的。
板子需要写明接口格式,复杂度和使用条件。
就这道题来说。
首先我发现备的板子二分写错了,于是我把整个板子学了一边。
然后发现其实没错,错的是它的凸包是逆时针存的,我的凸包板子是顺时针的。于是我把板子改成顺时针了。
真正错的是intriangle函数的参数传错了。并且这个板子本身传入的参数也没说清楚,它要传的是某点到凸包第一个点的方向向量。orz
最后我把凸包板子共线的问题也研究透了orz
完整的极角排序cmp函数会将共线的向量按照其长度排序。(这是顺时针版,其中return (v1 | v2) < 0;
的不等号控制方向。)
bool cmpr(pt const &a, pt const &b) {
pt v1 = a - L[0], v2 = b - L[0];
if ((v1 | v2) == 0)return v1.sqrLen() < v2.sqrLen();
return (v1 | v2) < 0;
}
这样做出来的凸包会存在点共线。(如果输入保证不共线,那可以省略if ((v1 | v2) == 0)return v1.sqrLen() < v2.sqrLen();
)
如果想去掉这些点,凸包就要这么求。( ≤ \le ≤)
while (ch.size() > 1 && (L[i] - ch.back() | ch.back() - ch[ch.size() - 2]) <= 0)ch.pop_back();
否则
while (ch.size() > 1 && (L[i] - ch.back() | ch.back() - ch[ch.size() - 2]) < 0)ch.pop_back();
代码
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
#include<iostream>
using namespace std;
#define rep(i,j,k) for(int i = (int)j;i <= (int)k;i ++)
#define debug(x) cerr<<#x<<":"<<x<<endl
#define pb push_back
#define FAST_IO ios::sync_with_stdio(0); cin.tie(0)
const int MAXN = 1000000;
const double EPS = 1e-9;
const double eps = 1e-9;
const int N = 100000 + 5;
struct pt {
long long x, y;
pt(long long _x = 0, long long _y = 0) :x(_x), y(_y) {}
pt operator+(const pt & p) const { return pt(x + p.x, y + p.y); }
pt operator-(const pt & p) const { return pt(x - p.x, y - p.y); }
bool operator<(const pt & p) const { return x < p.x || (x == p.x && y < p.y); }
long long operator*(pt o) { return x * o.x + y * o.y; }
long long operator|(pt o) { return x * o.y - o.x * y; }
long long cross(const pt & p) const { return x * p.y - y * p.x; }
long long dot(const pt & p) const { return x * p.x + y * p.y; }
long long cross(const pt & a, const pt & b) const { return (a - *this).cross(b - *this); }
long long dot(const pt & a, const pt & b) const { return (a - *this).dot(b - *this); }
long long sqrLen() const { return this->dot(*this); }
};
pt L[N], S[N];
vector<pt> seq;
int n;
bool pointInTriangle(pt a, pt b, pt c, pt point) {
long long s1 = abs(a.cross(b, c));
long long s2 = abs(point.cross(a, b)) + abs(point.cross(b, c)) + abs(point.cross(c, a));
return s1 == s2;
}
int sgn(long long val) {
return val > 0 ? 1 : (val == 0 ? 0 : -1);
}
bool lexComp(const pt & l, const pt & r) {
return l.x < r.x || (l.x == r.x && l.y < r.y);
}
void prepare(vector<pt> & points) {
n = points.size();
n--;
seq.resize(n);
for (int i = 0; i < n; i++)
seq[i] = points[i + 1] - points[0];
seq.push_back(seq.front());
}
bool pointInConvexPolygon(pt point) {
if (seq[0].cross(point) != 0 && sgn(seq[0].cross(point)) != sgn(seq[0].cross(seq[n - 1])))
return false;
if (seq[n - 1].cross(point) != 0 && sgn(seq[n - 1].cross(point)) != sgn(seq[n - 1].cross(seq[0])))
return false;
if (seq[0].cross(point) == 0)
return seq[0].sqrLen() >= point.sqrLen();
int l = 0, r = n ;
while (l <= r) {
int mid = (l + r) / 2;
int pos = mid;
if (seq[pos].cross(point) >= 0)r = mid - 1;
else l = mid + 1;
}
int pos = r;
return pointInTriangle(pt(0,0),seq[pos], seq[pos + 1], point);
}
vector<pt> ch;
bool cmpr(pt const &a, pt const &b) {
pt v1 = a - L[0], v2 = b - L[0];
if ((v1 | v2) == 0)return v1.sqrLen() < v2.sqrLen();
return (v1 | v2) < 0;
}
void getCH() {
sort(L + 1, L + n, cmpr);
ch.push_back(L[0]);
rep(i, 1, n - 1) {
while (ch.size() > 1 && (L[i] - ch.back() | ch.back() - ch[ch.size() - 2]) <= 0)ch.pop_back();
ch.push_back(L[i]);
}
}
int main() {
FAST_IO;
int l, s;
while (cin >> l) {
ch.clear();
seq.clear();
rep(i, 0, l - 1) { cin >> L[i].x >> L[i].y; }
cin >> s; rep(i, 1, s) { cin >> S[i].x >> S[i].y; }
int pos = 0;
sort(L, L + l);
n = l;
getCH();
int ans = 0;
prepare(ch);
rep(i, 1, s) {
if (pointInConvexPolygon(S[i] - ch[0])) {
//cout << S[i].x << ' ' << S[i].y <<'#'<< endl;
ans++;
};
}
cout << ans << endl;
}
// system("pause");
cin >> l;
}
死亡样例
凸包共线,并且询问点在这条边上的样例。
这是板子默认凸包上的点不共线(准确地说,是不和凸包第一个点共线)导致的。
14
4 4
5 3
5 5
6 5
7 1
7 4
7 7
8 2
8 6
8 8
9 4
9 5
9 6
9 7
29
1 8
2 2
2 4
2 6
3 1
3 4
4 2
4 3
5 1
5 4
5 7
6 1
6 2
6 3
6 4
6 6
7 2
7 3
7 5
9 1
9 2
9 3
9 8
8 7
8 5
8 4
8 3
8 1
7 6
凸包:
10
4 4
5 5
7 1
7 7
8 2
8 8
9 4
9 5
9 6
9 7
#######S#
#S#S#S###
S##S#####
#SSL#####
S#LSL#S##
SSSSLX###
LSSLSSL##
SLSSSLSL#
SSSLLLLS#
12
5 4
6 2
6 3
6 4
7 2
7 3
7 5
7 6
8 3
8 4
8 5
8 7
6 6***