2020-2021 ACM-ICPC Brazil Subregional Programming Contest M. Machine Gun

在二维平面上给出 $n$ 个敌军的坐标 $A_i(x_i, y_i)$, 再进行q次强制在线询问:

每次询问将给出一个炮台坐标 $(x_m, y_m)$, 需找出所有能被坐标 $(x_m, y_m)$ 攻击到的敌军的下标. 这里定义炮台 $(x_m, y_m)$ 能攻击到的范围是 $y = y_m \pm \frac{x - x_m}{2}$ 所夹出来的右半部分, 包括边界.

接下来将这些下标按照升序排序, 记为$i_0, i_1, \dots i_k$

计算结果 $(\sum_{j=0}^{k-1} i_j \cdot 5782344^j) \mod 10^9 + 7$, 输出结果.

$1 \le x_i, y_i \le 10^9, 1 \le n, q, \le 10^5$

保证所有查询中被攻击到的敌军总数不超过 $10^6$, 保证$x_i > 0, x_m < 0$

有两种考虑方式.

施鹏飞学长讲的:

注意到每个炮台所能攻击到的范围角度是固定的, 所以为了更好地刻画炮台 攻击的覆盖范围, 我们可以将炮台的攻击范围投影到$x=0$这条线上; 类似地, 将每个敌军以同样的角度都投影到$x=0$这条线上. 如果两条线段有交集, 那么 该炮台就可以攻击到这个敌军.

线段
线段

问题就等价地转化为: 在数轴上给出 $n$ 个区间, 再进行 $q$ 次 查询, 每次查询给出一个区间, 询问 $n$ 个区间中有哪些区间与询问的区间相交.

形式化一下, 我们要找这样的区间: $l_i \le r_m, r_i \ge l_m$, 其中 $[l_m, r_m]$是询问区间.

将坐标进行伸缩变换:

  1. 把两条边的夹角改成直角: $y \to 2y$
  2. 把 $x$ 轴 $y$ 轴顺时针转 $\frac{\pi}{4}$: $(x, y) \to (x - y, x + y)$

总的来说, 把 $(x, y) \to (x - 2y, x + 2y)$.

也就是说, 令每个点的坐标为 $\begin{cases} x’ = x - 2y \\ y’ = x + 2y \end{cases}$

也就是得到下面这种东西:

坐标
坐标

然后找这样的点: $x_i’ \ge x_m’, y_i’ \ge y_m'$

实际上两者没有本质区别, 最后得到的关系都是一样的, 只是思考方式的不同.

当然还有第三种方法, 就是直接推算满足什么样的条件的点会被炮塔打到. 得到的结果是一样的.

到这里就转换成了二维偏序问题. 先对一维排序, 然后再放到数据结构上排第二维.

这里需要的不是个数, 而是所有的点的下标. 所以考虑用线段树, 每个节点开一个 vector 按第二维的顺序存 第二维原下标(需要第二维排序, 需要原下标).

二分确定第一维的范围, 在线段树上区间查询第二维即可. 二维偏序就是这个套路.

复杂度 $O(n \log n + q \log^2 n)$

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
const int P = 1e9 + 7;
const LL BASE = 5782344;

const int MAXN = 1e5+10;

int Plus(LL a, LL b) {
	return a + b >= P ? (a + b) % P : a + b;
}
int Mult(LL a, LL b) {
	return a * b >= P ? a * b % P : a * b;
}

struct Seg {
	LL l, r, id;
	bool operator < (const Seg &S) const {
		return r == S.r ? l == S.l ? id < S.id : l < S.l : r < S.r;
	}
} segs[MAXN];

int n, q;
LL p = 0;

Seg calc_seg(LL x, LL y, int id) {
	LL l = 2 * y - x, r = 2 * y + x;
	if (l > r)
		swap(l, r);
	return Seg{l, r, id};
}

struct SegTree {
	struct Node {
		int l, r, mid;
		vector<PLI> v;
	};
	vector<Node> tree;
	SegTree(int _n, Seg data[]) {
		tree.resize(_n << 2);
		build(data, 1, _n);
	}
	void push_up(int u) {
		vector<PLI> &lv = tree[LCH(u)].v, &rv = tree[RCH(u)].v, &v = tree[u].v;
		int i = 0, j = 0, len_l = lv.size(), len_r = rv.size();
		while (i < len_l && j < len_r) {
			if (lv[i] < rv[j])
				v.push_back(lv[i++]);
			else
				v.push_back(rv[j++]);
		}
		while (i < len_l)
			v.push_back(lv[i++]);
		while (j < len_r)
			v.push_back(rv[j++]);
	}
	void build(Seg data[], int l, int r, int u = 1) {
		tree[u] = Node{l, r, (l + r) >> 1};
		tree[u].v.clear();
		if (l == r)
			tree[u].v.push_back(make_pair(data[l].l, data[l].id));
		else {
			build(data, l, tree[u].mid, LCH(u));
			build(data, tree[u].mid + 1, r, RCH(u));
			push_up(u);
		}
	}
	void add_ans(set<LL> &res, LL r, int u) {
		for (auto x : tree[u].v) {
			if (x.first > r)
				break;
			else
				res.insert(x.second);
		}
	}
	void query(set<LL> &res, LL rr, int l, int r, int u = 1) {
		if (l == tree[u].l && r == tree[u].r)
			add_ans(res, rr, u);
		else {
			if (l > tree[u].mid)
				query(res, rr, l, r, RCH(u));
			else if (r <= tree[u].mid)
				query(res, rr, l, r, LCH(u));
			else {
				query(res, rr, l, tree[u].mid, LCH(u));
				query(res, rr, tree[u].mid + 1, r, RCH(u));
			}
		}
	}
};

int main() {
	scanf("%d%d", &n, &q);
	for (int i = 1; i <= n; i++) {
		LL x, y;
		scanf("%lld%lld", &x, &y);
		segs[i] = calc_seg(x, y, i);
	}
	sort(segs + 1, segs + 1 + n);

	SegTree T(n, segs);
	while (q--) {
		LL a, b;
		scanf("%lld%lld", &a, &b);
		LL x = - 1 - Plus(p, a), y = Plus(p, b);
		Seg s = calc_seg(x, y, 0);
		int pos = lower_bound(segs + 1, segs + 1 + n, Seg{-INF, s.l, 0}) - segs;
		p = 0;
		if (pos <= n) {
			set<LL> res;
			res.clear();
			T.query(res, s.r, pos, n);
			LL power = 1;
			for (auto x : res) {
				p = Plus(p, Mult(power, x));
				power = Mult(power, BASE);
			}
		}
		printf("%lld\n", p);
	}
	return 0;
}