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;
}
posted @ 2023-01-13 22:09  DCH233  阅读(21)  评论(0编辑  收藏  举报