CF1583G Omkar and Time Travel
Description
有 \(n\) 个传送点,每个传送点有一个入口 \(a_i\) 和一个出口 \(b_i\) ( \(a_i > b_i\) ),初始时全部没做标记,并且只有没有标记的传送点会传送,每次传送之后标记该传送点,同时取消所有 \(b_j > b_i\) 的 \(j\) 的传送点的标记。
现在给定 \(m\) 个编号,问这下编号的传送点全部被标记时,最小的传送次数。
Solution
能明显感觉到有些区间是能够反复影响一些另外的区间的,来分类讨论一下:
- 两个区间没有交集
- 那显然走到前面一个传送点的时候和后者完全没有关系,所以互不影响
- 两个区间有交集但是不互相包含
- 同上的情况,因为我们只有走过 \(a\) 才能跑到 \(b\) ,所以即便是有交也不影响。
- 包含
- 这样的话我们会先走到小区间,然后传送完之后走到大区间,这样会取消小区间的标记,然后再走一次小区间才能走出去。
同时这也证明我们必能在有限时间内走出这个鬼地方。
现在发现我们只是讨论了两个区间的请款,多个的话肯定会复杂的要死,所以干脆我们设一个 \(f_i\) 表示从 \(i\) 从 \(b_i\) 到能完全走出 \(a_i\) 需要的传送次数。
那么也就是说所有 \(i\) 包含的区间全部要这样来一遍,因为考虑到如果设 \(b_i\) 到走出 \(a_i\) 的话包含了第一次到达 \(a_i\) 前的那些小区间走的次数,会计重,所以改一下定义:
\(f_i\) 表示从 \(a_i\) 传送回去到能完全走出 \(a_i\) 需要的传送次数。
这样我们可以理解为走完一个大区间分为两步:
-
把所有包含的小区间标记完要的次数。
-
在 \(i\) 传送之后再把包含的小区间重新再走一遍。
所以明显能推出这样的一个小式子(其中 \(S_i\) 表示 \(i\) 包含的区间集合):
可以树状数组维护,把所有区间按照 \(b_i\) 从小到大排序,从前往后扫,每次就把 \(f_i\) 挂在 \(a_i\) 上,然后查找所有 \(a_j\) 比它大的区间。
因为前面已经挂了贡献的区间 \(b_j\) 必定必 \(b_i\) 小,树状数组查找只找 \(a_j\) 比 \(a_i\) 大的区间,那就是所有包含 \(i\) 的区间,所以这样就类似二维偏序的完成了对 \(f_i\) 的统计。
然后是统计答案:
-
我们发现一个要标记的区间 \(i\) 前面所有区间(有交也算)必然要全部标记。
-
自己包含的区间虽然有贡献但是如果算上自己被标记的传送次数 \(1\) 的话,其实就是 \(f_i\) 。
-
但是注意到包含自己的区间没有贡献,完全不需要参与进来。
所以总结一下,就是一个区间要贡献的话,就相当于他要是在一个规定的区间前面(有交也算),或者自己要被标记。
所以这样的话同样树状数组挂标记能搞定。
时间复杂度 \(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;
}