[CF] D. Array Collapse - Educational Codeforces Round 160 (Rated for Div. 2)

https://codeforces.com/contest/1913/problem/D

题目大意

给出一个 $n \ (1 \le n \le 3 \times 10^5)$ 排列 $p$。可以做如下操作任意次:

选出一个区间 $[l, r]$ 将其中除了最小元素以外的元素全部删掉。

问结果数组共有多少种。结果对 $998244353$ 取模。

简要题解

观察:

  1. 注意到 $1$ 永远删不掉。
  2. 考虑 $2$ 则如果想删掉 $2$ 只能靠选一个包含 $1, 2$ 的数组。也就是说如果结果中没有 $2$ 则也没有 $1, 2$ 之间的所有其他数。
  3. 对于 $i$ 我们找到其左右比它更大的最近的数,如果想删掉它,只能靠至少包含这两个数其中之一的数,如果某一侧没有比它更大的数,则无法通过这一侧将其删去。
  4. $2$ 对于越过 $1$ 的另一侧的数是没影响的。因为这样总会越过 $1$ 而有 $1$ 就够了。
  5. 扩展到更多的数,越过比它自己更大的数的区间是没有意义的。

这样其实我们已经知道了该如何算。由 $5$ 可知我们其实可以从小到大的分割处理区间。对于当前的区间 $l, r$ 我们找出其中最小的元素 $i$。 保留 $i$,则其实 $i$ 这里类似于 $1$ 对于整个数组,递归进入左右区间即可。 要删掉 $i$ 我们总要删掉其中一边,或者我们把两边数组都删掉(这是可以行的,因为可以先通过 $i$ 删掉一边,再通过 $i$ 和另一边更小的数 ($l, r$ 边界之外的数)来删掉 $i$)。

复杂度

$T$:$O(n \log n)$

$S$:$O(n \log n)$ 因为用了 sparse-table。

代码实现

#include <bits/stdc++.h>
using namespace std;

int io_=[](){ ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }();

using LL = long long;
using ULL = unsigned long long;
using LD = long double;
using PII = pair<int, int>;
using VI = vector<int>;
using MII = map<int, int>;

template<typename T> void cmin(T &x,const T &y) { if(y<x) x=y; }
template<typename T> void cmax(T &x,const T &y) { if(x<y) x=y; }
template<typename T> bool ckmin(T &x,const T &y) { 
    return y<x ? (x=y, true) : false; }
template<typename T> bool ckmax(T &x,const T &y) { 
    return x<y ? (x=y, true) : false; }
template<typename T> void cmin(T &x,T &y,const T &z) {// x<=y<=z 
    if(z<x) { y=x; x=z; } else if(z<y) y=z; }
template<typename T> void cmax(T &x,T &y,const T &z) {// x>=y>=z
    if(x<z) { y=x; x=z; } else if(y<z) y=z; }

// mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
// mt19937_64 rnd_64(chrono::system_clock::now().time_since_epoch().count());
template<typename T, typename CMP=less<T>>
struct SparseTable {
  constexpr static CMP cmp{};
  static T min(const T& a, const T& b) { return (cmp(a, b) ? a : b); }
  static int lg(int x) { return 31 - __builtin_clz(x); }

  vector<vector<T> > st;

  SparseTable(const vector<T>& a) {
    int n = a.size();
    int b = lg(n);

    st.push_back(a);
    for (int i = 1; i <= b; i++) {
      int m = n - (1 << i) + 1;
      vector<T> b(m);
      for (int j = 0; j < m; j++) {
        b[j] = min(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]);
      }
      st.push_back(b);
    }
  }

  T query(int l, int r) {
    int i = lg(r - l + 1);
    return min(st[i][l], st[i][r - (1 << i) + 1]);
  }
};

template<typename Func> struct YCombinatorResult {
  Func func;
  template<typename T>
  explicit YCombinatorResult(T &&func) : func(std::forward<T>(func)) {}
  template<class ...Args> decltype(auto) operator()(Args &&...args) {
    return func(std::ref(*this), std::forward<Args>(args)...);
  }
};
template<typename Func> decltype(auto) y_comb(Func &&fun) {
  return YCombinatorResult<std::decay_t<Func>>(std::forward<Func>(fun));
}

template<long long Mo=998244353> struct ModInt {
  static long long MO;
  static void setMo(long long mo) { MO = mo; }
  long long x;
  ModInt(long long x=0) : x(x){ norm(); }
  friend istream &operator>>(istream& in, ModInt &B) { in>>B.x; return in; }
  friend ostream &operator<<(ostream& out, const ModInt &B) { 
    out<<B.x; return out; }
  // ModInt operator=(int x_) { x=x_; norm(); return *this; }
  void norm() { x = (x%MO + MO) % MO; }
  long long get() { return x; }

  ModInt operator-() const { return ModInt(MO - x); }
  ModInt operator+=(const ModInt &B) { x+=B.x; if(x>=MO) x-=MO; return *this; }
  ModInt operator-=(const ModInt &B) { x-=B.x; if(x<0) x+=MO; return *this; }
  ModInt operator*=(const ModInt &B) { x=x*B.x%MO; return *this; }
  ModInt operator+(const ModInt &B) const { ModInt ans=*this; return ans+=B; }
  ModInt operator-(const ModInt &B) const { ModInt ans=*this; return ans-=B; }
  ModInt operator*(const ModInt &B) const { ModInt ans=*this; return ans*=B; }
  ModInt operator^(long long n) const  {
    ModInt a=*this; ModInt ans(1);
    while(n) { if(n&1) ans*=a; a*=a; n>>=1; }
    return ans;
  }
  ModInt inv() const { return (*this)^(MO-2); } // if MO is prime
  ModInt operator/=(const ModInt &B) { (*this)*=B.inv(); return *this; }
  ModInt operator/(const ModInt &B) const { ModInt ans=*this; return ans/=B; }

  bool operator<(const ModInt &B) const { return x<B.x; }
  bool operator==(const ModInt &B) const { return x==B.x; }
  bool operator!=(const ModInt &B) const { return x!=B.x; }
};
template<long long Mo> long long ModInt<Mo>::MO = Mo;
typedef ModInt<998244353> Mint;
// typedef ModInt<1'000'000'007> Mint;
/*
---------1---------2---------3---------4---------5---------6---------7---------
1234567890123456789012345678901234567890123456789012345678901234567890123456789
*/

void solve() {
  int n; cin >> n;
  vector<PII> p(n);
  for (int i = 0; i < n; i++) {
    cin >> p[i].first;
    p[i].second = i;
  }
  SparseTable st(p);

  Mint ans = y_comb([&](auto dfs, int l, int r) -> Mint {
    if (l == r) {
      Mint ans = 1; // keep
      if (l || r < n - 1) ans += 1; // remove with left or right
      return ans;
    }
    int mid = st.query(l, r).second;
    Mint lans = (l < mid ? dfs(l, mid - 1) : Mint(1));
    Mint rans = (mid < r ? dfs(mid + 1, r) : Mint(1));
    Mint ans = lans * rans; // keep
    if (l) ans += (rans - 1); // remove left
    if (r < n - 1) ans += (lans - 1); // remove right
    if (l || (r < n - 1)) ans += 1; // remove both
    return ans;
  })(0, n - 1);

  cout << ans << '\n';
}

int main() {
  int t = 1; 
  cin >> t;
  while (t--) {
    solve();
  }
  return 0;
}
Prev: [CF] A. Broken Keyboard - Educational Codeforces Round 75 (Rated for Div. 2)
Next: [CF] C. Game with Multiset - Educational Codeforces Round 160 (Rated for Div. 2)