正睿二十连测
B
时间:\(40min\)。
用个并查集之类的东西维护一下边即可。可惜因为 map 常数巨大 TLE 了 \(8pts\)。
变成 unordered_map 或者加快读都能轻松通过。
C
方向搞错了,瞄了眼题解 \(35min\) 就做出来了。
给定 \(n\) 个点 \(m\) 条边的有向图,问由多少个区间 \([l, r]\) 满足任意 \(u \in [l, r]\) 的出边 \((u, v)\)满足到达的点 \(v\) 也在 \([l, r]\) 内。
首先我们求出 \(mi_u\) 以及 \(mx_u\) 表示 \(u\) 连的 \(v\) 中最小的以及最大的,表示如果 \(u\) 在区间内,则需满足 \(l \le mi_u, mx_u \le r\)。
所以对于 \([l, r]\) 来说,只需要满足 \(\min\limits_{i = l}^r \{mi_i\} \ge l, \max\limits_{i = l}^r\{mx_i\} \le r\) 即可。
而这种区间 \(\min, \max\) 问题一般就分治或者单调栈(笛卡尔树)比较常用。实际上都可以,因为这玩意比较简单,复杂的 DP 之类可能分治比较好,因为分治能把一段区间的 \(\min, \max\) 转化成一个前缀和一个后缀,就简单了很多,这个题应该搞两个指针即可。
我们来讲讲单调栈怎么做?
考虑枚举 \(r\),显然满足 \(\max\limits_{i = l}^r\{mx_i\} \le r\) 的 \(l\) 是一段区间,只要找到最后一个 \(mx_i > r\) 的位置 \(x\) 即可。这其实很简单,可以倍增,也可以用单调栈维护 \(1 \sim r\) 的后缀最大值然后二分。
在考虑第一个条件,假设我们求出了对于 \(r - 1\) 合法的所有 \(l\),那么加入 \(r\) 后只需要先把 \(r\) 加入,把 \(l > mi_r\) 的弹掉,显然把合法的 \(l\) 也是后面的一段,用栈维护即可。
最后我们只需要在这个栈中找到有多少个位置 \(> x\) 即可,显然可以二分。
笨一点可以将两个条件转化为 \(l \ge p_r, r \le q_l\) 的形式,然后二维数点,反正怎么做都行。
时间复杂度:\(O(n \log n)\)。
来讲讲我是如何 sb 没做出来的。
首先我求出 \(mi_i, mx_i\),我就想这个区间是不断扩展的,所以我就写了个线段树优化建图把这些点都缩起来的得到一个 DEG,这样就知道选了一个点后至少要选哪个区间 \([L_i, R_i]\),然后尝试 \(L_i - 1\) 向 \(R_i\) 连边,用 bitset 记录每个 \(r\) 可以 \(l\),结果发现 \([L_i, R_i]\) 可以相交然后就退化成 \(O(n^3/w)\) 的 \(25pts\) 就废了。
所以第一步就想错了。。。其实对于分析一下 \([l, r]\) 有贡献的条件就化简成一个十分“套路”的式子,想怎么做怎么做了。