鱼死网破(*)
很容易想到对于每个点和每个墙上的两端相连,形成这样的图形:
\(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;
}