凸包内点的数量 J-Saint John Festival @ 2015-2016 ACM-ICPC Southwestern Europe Regional Contest (SWERC 15)

凸包内点的数量 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***

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Best KeyBoard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值