CF1583G Omkar and Time Travel

Description

\(n\) 个传送点,每个传送点有一个入口 \(a_i\) 和一个出口 \(b_i\)\(a_i > b_i\) ),初始时全部没做标记,并且只有没有标记的传送点会传送,每次传送之后标记该传送点,同时取消所有 \(b_j > b_i\)\(j\) 的传送点的标记。

现在给定 \(m\) 个编号,问这下编号的传送点全部被标记时,最小的传送次数。

Solution

能明显感觉到有些区间是能够反复影响一些另外的区间的,来分类讨论一下:

  1. 两个区间没有交集
  • 那显然走到前面一个传送点的时候和后者完全没有关系,所以互不影响
  1. 两个区间有交集但是不互相包含
  • 同上的情况,因为我们只有走过 \(a\) 才能跑到 \(b\) ,所以即便是有交也不影响。
  1. 包含
  • 这样的话我们会先走到小区间,然后传送完之后走到大区间,这样会取消小区间的标记,然后再走一次小区间才能走出去。

同时这也证明我们必能在有限时间内走出这个鬼地方。

现在发现我们只是讨论了两个区间的请款,多个的话肯定会复杂的要死,所以干脆我们设一个 \(f_i\) 表示从 \(i\)\(b_i\) 到能完全走出 \(a_i\) 需要的传送次数。

那么也就是说所有 \(i\) 包含的区间全部要这样来一遍,因为考虑到如果设 \(b_i\) 到走出 \(a_i\) 的话包含了第一次到达 \(a_i\) 前的那些小区间走的次数,会计重,所以改一下定义:

\(f_i\) 表示从 \(a_i\) 传送回去到能完全走出 \(a_i\) 需要的传送次数。

这样我们可以理解为走完一个大区间分为两步:

  1. 把所有包含的小区间标记完要的次数。

  2. \(i\) 传送之后再把包含的小区间重新再走一遍。

所以明显能推出这样的一个小式子(其中 \(S_i\) 表示 \(i\) 包含的区间集合):

\[f_i = 1 + \sum_{j \in S_i} f_j \]

可以树状数组维护,把所有区间按照 \(b_i\) 从小到大排序,从前往后扫,每次就把 \(f_i\) 挂在 \(a_i\) 上,然后查找所有 \(a_j\) 比它大的区间。

因为前面已经挂了贡献的区间 \(b_j\) 必定必 \(b_i\) 小,树状数组查找只找 \(a_j\)\(a_i\) 大的区间,那就是所有包含 \(i\) 的区间,所以这样就类似二维偏序的完成了对 \(f_i\) 的统计。


然后是统计答案:

  1. 我们发现一个要标记的区间 \(i\) 前面所有区间(有交也算)必然要全部标记。

  2. 自己包含的区间虽然有贡献但是如果算上自己被标记的传送次数 \(1\) 的话,其实就是 \(f_i\)

  3. 但是注意到包含自己的区间没有贡献,完全不需要参与进来。

所以总结一下,就是一个区间要贡献的话,就相当于他要是在一个规定的区间前面(有交也算),或者自己要被标记。

所以这样的话同样树状数组挂标记能搞定。

时间复杂度 \(O(n\log n)\)

(注:我的程序 \(a\)\(b\) 是反过来的,并且我好像写的是按照 \(b\) 排序,不过问题不大,都能做)

Code

/*

*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 4e5 + 10, mod = 1e9 + 7;
int n, m, sig[N], f[N], ans;
struct mdzz {
	int a, b, id;
	bool operator < (const mdzz &it) const {
		return a < it.a;
	}
} l[N];
inline int read() {
	char ch = getchar();
	int s = 0, w = 1;
	while (!isdigit(ch)) {if (ch == '-') w = -1; ch = getchar();}
	while (isdigit(ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar();}
	return s * w;
}
struct SGT {
	int c[N];
	inline int lowbit(int x) {return x & (-x);}
	inline void add(int x, int k) {
		for (; x <= (n << 1); x += lowbit(x)) (c[x] += k) %= mod;
	}
	inline int query(int x) {
		int tmp = 0;
		for (; x; x -= lowbit(x)) (tmp += c[x]) %= mod;
		return tmp;
	}
	inline int query(int x, int y) {
		return (query(y) - query(x) + mod) % mod;
	}
} feq, it;
int main() {
	n = read();
	for (int i = 1; i <= n; ++i) {
		l[i] = (mdzz) {read(), read(), i};
	}
	sort(l + 1, l + 1 + n);
	m = read();
	for (int i = 1; i <= m; ++i) sig[read()] = 1;
	for (int i = n; i; --i) {
		f[i] = (1 + feq.query(l[i].b)) % mod;
		feq.add(l[i].b, f[i]);
		it.add(l[i].b, sig[l[i].id]);
		if (it.query(l[i].b - 1, n << 1)) (ans += f[i]) %= mod;
	}
	printf("%d\n", ans);
	return 0;
}
posted @ 2022-02-13 12:17  Illusory_dimes  阅读(72)  评论(0编辑  收藏  举报