「省选测试」拾

鱼死网破(*)

很容易想到对于每个点和每个墙上的两端相连,形成这样的图形:

「省选测试」拾

\(A\) 和 \(C\) 区域一定会被看到,而 \(B\) 区域是无法被看见的,不会对答案造成贡献

我们不妨去求,对于一个 \(x\) 轴下方的点,不能被多少个点看见?

我们将这个点与线段的两端连线,能不能被看见分别是下面两种情况:

「省选测试」拾
「省选测试」拾

不妨将线段的两端端点分别向上面的点建向量

「省选测试」拾
「省选测试」拾

发现对于不同的情况,在同一个端点的向量的位置关系是不同的

可以利用差分的思想,将每一个 \(x\) 轴上方的点与 \(k\) 的线段的左端形成的向量存起来,权值为 \(1\),将每一个 \(x\) 轴上方的点与 \(k\) 的线段的有右端形成的向量存起来,权值为 \(-1\)

对于有重合的情况,如下图

「省选测试」拾

会出现贡献重复计算的情况,所以我们在对每个端点存向量的时候合并一下就好了

最后询问的时候,求出这个点与每个端点的极角,二分一下就好了,仔细手摸一下就很好理解了

如果你觉得你写得很对,然后爆零了,不妨检查一下自己有没有开 \(long\; long\)

代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>

#define int long long 

using namespace std;

const int maxn = 2e5 + 50, INF = 0x3f3f3f3f;

inline int read () {
	register int x = 0, w = 1;
	register char ch = getchar ();
	for (; ch < '0' || ch > '9'; ch = getchar ()) if (ch == '-') w = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar ()) x = x * 10 + ch - '0';
	return x * w;
}

int n, k, m, opt;

struct Node {
	int x, y;
	Node () {}
	Node (register int a, register int b) { x = a, y = b; }
	friend bool operator < (const Node &a, const Node &b) {  return a.x * b.y - a.y * b.x > 0; } // 顺负逆正,相当与是逆时针排序
} a[maxn];

vector <Node> vec[maxn];

struct Wall {
	int x1, x2, y;
} b[maxn];

struct Line {
	int id;
	Node a;
	Line () {}
	Line (register int x, register Node y) { id = x, a = y; }
	friend bool operator < (const Line &x, const Line &y) { return x.a < y.a; } // 同样是逆时针排序
} c[maxn];

signed main () {
	n = read(), k = read(), m = read(), opt = read();
	for (register int i = 1; i <= n; i ++) a[i].x = read(), a[i].y = read();
	for (register int i = 1; i <= k; i ++) b[i].x1 = read(), b[i].x2 = read(), b[i].y = read();
	for (register int i = 1; i <= n; i ++) {
		register int cnt = 0;
		for (register int j = 1; j <= k; j ++) {
			if (b[j].y >= a[i].y) continue;
			c[++ cnt] = Line (j, Node (a[i].x - b[j].x1, a[i].y - b[j].y));
			c[++ cnt] = Line (j + k, Node (a[i].x - b[j].x2, a[i].y - b[j].y));
		}
		sort (c + 1, c + cnt + 1);
		register int res = 0;
		for (register int j = 1; j <= cnt; j ++) {
			res += c[j].id <= k ? 1 : -1;
			if (c[j].id <= k && res == 1) vec[c[j].id].push_back (c[j].a); // 合并的过程,可以自己手摸一下
			else if (c[j].id > k && res == 0) vec[c[j].id].push_back (c[j].a);
		}
	}
	for (register int i = 1; i <= k << 1; i ++) sort (vec[i].begin (), vec[i].end ());
	register int last = 0;
	while (m --) {
		register int x = read() ^ last * opt, y = read() ^ last * opt, ans = 0; // ans求得是看不见这个点的个数
		for (register int i = 1; i <= k; i ++) ans += upper_bound (vec[i].begin (), vec[i].end (), Node (b[i].x1 - x, b[i].y - y)) - vec[i].begin (); // 注意取等,因为线段的端点是包括在墙内的
		for (register int i = k + 1; i <= k << 1; i ++) ans -= lower_bound (vec[i].begin (), vec[i].end (), Node (b[i - k].x2 - x, b[i - k].y - y)) - vec[i].begin ();
		printf ("%lld\n", last = n - ans);
	}
	return 0;
}

漏网之鱼(escape)

浑水摸鱼(waterflow)

上一篇:vue入门教程(5)——vue中vue-router的使用


下一篇:【STM32F407】第3章 PHY芯片和STM32的MAC基础知识