CF1774G Segment Covering
CF1774G Segment Covering
太不容易了,终于通过了!!!!!
先把所有右端点减一,然后有 \([a,b] \cup [b+1,c] = [a,c]\)。
要求的这个东西一看就需要奇偶相消的一些操作。先想象询问 \([1,n]\) 怎么做。考虑容斥,钦定 \(k\) 个点是没有被覆盖的,假设现在有 \(x\) 条线段可选可不选,那么由于奇偶相消,所以当且仅当 \(x=0\) 时有 \((-1)^k\) 的贡献。
现在我们要求选定一个点集 \(S\),使得所有的线段上至少有一个点在点集中,贡献为 \((-1)^{|S|}\)。在线段上这个一看就是可以用DP来完成。具体地,设 \(f_i\) 表示满足右端点在 \(i\) 左侧的点的限制且最后一个选中的点为 \(i\)。维护\(p_x\) 表示右端点在 \(x\) 左侧的线段中左端点的最大值。那么转移比较显然。
\[f_i = -\sum_{j=p_x}^{i-1}f_j
\]
这个式子一看就是前缀和优化嘛~然后这题一个非常巧妙的地方就来了设 \(s_k\) 为 \(f\) 的前缀和,那么可以得到
\[s_k - s_{k-1} = - s_{k-1} + s_{p_k - 1} \Rightarrow s_k = s_{p_k - 1}
\]
这样就可以连边然后倍增跳了。
#include <cstdio>
#include <algorithm>
namespace IO {
#define isdigit(x) (x >= '0' && x <= '9')
template<typename T>
inline void read(T &x) {
x = 0; char ch = getchar(); int f = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
if(f) x = -x;
}
template<typename T>
inline void write(T x) {
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
#undef isdigit
}
using namespace IO;
const int N = 2e6 + 10;
const int inf = 1e9;
int n, q;
struct Itv {
int l, r;
}a[N];
int len, ds[N], tot = 0;
int lp[N], fa[N][19];
inline int query(int l, int r) {
for(int i = 18; ~i; --i)
if(fa[r][i] >= l) r = fa[r][i];
if(r >= l) r = fa[r][0];
return r == l - 1;
}
int main() {
read(n), read(q);
for(int i = 1; i <= n; ++i) {
read(a[i].l), read(a[i].r), --a[i].r,
ds[++tot] = a[i].l, ds[++tot] = a[i].r;
ds[++tot] = a[i].l - 1, ds[++tot] = a[i].l + 1,
ds[++tot] = a[i].r - 1, ds[++tot] = a[i].r + 1;
}
ds[++tot] = -5, ds[++tot] = -4, ds[++tot] = -3, ds[++tot] = -2; //防越界
std::sort(ds + 1, ds + tot + 1);
len = std::unique(ds + 1, ds + tot + 1) - ds - 1;
for(int i = 1; i <= n; ++i) {
a[i].l = std::lower_bound(ds + 1, ds + len + 1, a[i].l) - ds;
a[i].r = std::lower_bound(ds + 1, ds + len + 1, a[i].r) - ds;
}
for(int i = 0; i <= len; ++i)
lp[i] = 1;
for(int i = 1; i <= n; ++i)
lp[a[i].r] = std::max(lp[a[i].r], a[i].l);
for(int i = 1; i <= len; ++i)
lp[i] = std::max(lp[i - 1], lp[i]), fa[i][0] = lp[i - 1] - 1;
for(int i = 1; i <= 18; ++i)
for(int j = 1; j <= len; ++j)
fa[j][i] = fa[fa[j][i - 1]][i - 1];
for(int i = 1, l, r, ql, qr; i <= q; ++i) {
read(ql), read(qr);
--qr;
l = std::lower_bound(ds + 1, ds + len + 1, ql) - ds;
r = std::lower_bound(ds + 1, ds + len + 1, qr) - ds;
if(ds[l] != ql || ds[r] != qr) {
puts("0");
continue;
}
int ans = query(l, r) - query(l, lp[r] - 1);
if(ans == -1) ans = 998244352;
printf("%d\n",ans);
}
return 0;
}