CF1707E Replace

CF1707E Replace

完全没想到从这种角度考虑性质。

一开始的思路是把每种区间表示为一个节点建树,然后答案就是深度,但是很显然节点数是平方级别的,所以就寄了。尝试使用数据结构维护,但是做不到,很伤心。

看一眼题解,有一个神仙性质 \(f(l,r) = \bigcup_{i=l}^{r-1} f(i,i+1)\)

这个怎么证明呢,考虑区间的最大值和最小值,中间的部分都可以被覆盖,所以就是对的。

那么可以归纳出一个更强的结论 \(f^k(l,r) = \bigcup_{i=l}^{r-1} f^k(i,i+1)\)

证明和上面类似。那么就有了一个很优美的方式表示 \(f^k(l,r)\)

现在想要知道最小的 \(k\) 使得 \(f^k(l,r) = (1,n)\),直接倍增就可以了。

这个故事告诉我们:处理关于区间的操作时可以考虑大区间和小区间之间的关系,进而在处理询问时将小区间组合为大区间维护信息。

#include <cstdio>
#include <iostream>

using namespace std;

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 = 2e5 + 10;
const int P = 20;
int n, q;
int a[N];
int lg[N];

struct itv {
  int l, r;
  inline itv operator + (itv a) const{
    a.l = min(a.l, l), a.r = max(a.r, r);
    return a;
  }
  inline bool operator == (itv a) const{
    return l == a.l && r == a.r;
  }
}t[N][P][P];

inline itv query(itv x, int p) {
  int l = x.l, r = x.r;
  int q = lg[r - l + 1];
  return t[l][q][p] + t[r - (1 << q) + 1][q][p];
}

int main() {
  read(n), read(q);
  for(int i = 1; i <= n; ++i)
    read(a[i]);
  for(int i = 2; i <= n; ++i)
    lg[i] = lg[i >> 1] + 1;
  for(int i = 1; i <= n; ++i) 
    t[i][0][0] = (itv){a[i], a[i]};
  for(int i = 1; i < P; ++i)
    for(int j = 1; j <= n - (1 << i) + 1; ++j)
      t[j][i][0] = t[j][i - 1][0] + t[j + (1 << (i - 1))][i - 1][0];
  for(int j = 1; j < P; ++j)
    for(int i = 0; i < P; ++i)
      for(int k = 1; k <= n; ++k)
        t[k][i][j] = query(t[k][i][j - 1], j - 1);
  itv ed = {1, n};
  for(int i = 1; i <= q; ++i) {
    itv x;
    read(x.l), read(x.r);
    if(x == ed) {
      puts("0");
      continue;
    }
    int ans = 1;
    for(int i = P - 1; ~i; --i) {
      if(!(query(x, i) == ed) && query(x, i).l) 
        x = query(x, i), ans += (1 << i);
    }
    if(!(query(x, 0) == ed)) puts("-1");
    else printf("%d\n",ans);
  }
  return 0;
}

upd on 2023.03.06: 杂谈讲了这题,交了一发惨遭卡常,把倍增数组换成小的维在前快了一千毫秒。

posted @ 2023-02-03 08:20  DCH233  阅读(27)  评论(0编辑  收藏  举报