珂朵莉树

珂朵莉树,简称 \(\text{odt}\),又称老司机树,用于区间推平或数据随机。其思想暴力,实现简单,名字好听,深受 \(\text{OIer}\) 喜爱。

CF896C Willem, Chtholly and Seniorious

区间推平会使最后的序列变成一段一段连续的数,那我们记一个结构体,保存每一段的端点极其权值,按左端点从小到大排序。

珂朵莉树核心是 splitassign 函数,用来分裂和合并。

split(x) 的意思是以 \(x\) 做切割,找到一个含有 \(x\) 的区间 \([l, r]\),将其分裂为 \([l, x - 1]\)\([x, r]\)。如果 \(x\) 本身就是一个区间的开头,就不用切割了,直接返回这个区间。

assign 则是合并。我们可以把整个 \(\text{set}\) 中所有要被合并掉的都删掉,然后插入一个新区间表示推平以后的结果。

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int N = 1e5 + 100;
const int Mod = 1000000007;

int n, m, seed, vmax, a[N];

struct odt {
  int l, r;
  mutable int v;
  odt(int l, int r = 0, int v = 0) : l(l), r(r), v(v) {}
  const bool operator < (const odt &e) const {
    return l < e.l;
  }
};

set<odt> s;

set<odt>::iterator split(int x) {
  auto it = s.lower_bound(odt(x));
  if (it != s.end() && it -> l == x) return it;
  it--; if (it -> r < x) return s.end();
  int l = it -> l, r = it -> r, v = it -> v;
  s.erase(it), s.insert(odt(l, x - 1, v));
  return s.insert(odt(x, r, v)).first;
}

void assign(int l, int r, int x) {
  set<odt>::iterator itr = split(r + 1), itl = split(l);
  s.erase(itl, itr), s.insert(odt(l, r, x));
}

void add(int l, int r, int x) {
  set<odt>::iterator itr = split(r + 1), itl = split(l);
  for (auto it = itl; it != itr; it++) it -> v += x;
}

int qpow(int a, int b, int mod) {
  if (!b) return 1;
  int tmp = qpow(a, b / 2, mod);
  if (b & 1) return tmp % mod * tmp % mod * a % mod;
  return tmp % mod * tmp % mod;
}

struct Node {
  int num, cnt;
  bool operator < (const Node &e) const {
    return num < e.num;
  }
  Node(int num, int cnt) : num(num), cnt(cnt) {}
};

int rnk(int l, int r, int x) {
  set<odt>::iterator itr = split(r + 1), itl = split(l); 
  vector<Node> v;
  for (auto it = itl; it != itr; it++) {
    v.push_back(Node(it -> v, it -> r - it -> l + 1));
  }
  sort(v.begin(), v.end());
  int i;
  for (i = 0; i < v.size(); i++) {
    if (v[i].cnt < x) x -= v[i].cnt;
    else break;
  }
  return v[i].num;
}

int getsum(int l, int r, int x, int y) {
  set<odt>::iterator itr = split(r + 1), itl = split(l);
  int ans = 0;
  for (auto it = itl; it != itr; it++) {
    ans = (ans + qpow(it -> v % y, x, y) * (it -> r - it -> l + 1) % y) % y;
  }  
  return ans;
}

int rnd() {
  int ret = seed;
  seed = (seed * 7 + 13) % Mod;
  return ret;
} 

signed main() {
  cin >> n >> m >> seed >> vmax;
  for (int i = 1; i <= n; i++) {
    a[i] = (rnd() % vmax) + 1;
    s.insert(odt(i, i, a[i]));
  }
  for (int i = 1, x, y; i <= m; i++) {
    int opt = (rnd() % 4) + 1, l = (rnd() % n + 1), r = (rnd() % n + 1);
    if (l > r) swap(l, r);
    if (opt == 3) x = (rnd() % (r - l + 1)) + 1;
    else x = (rnd() % vmax) + 1;
    if (opt == 4) y = (rnd() % vmax) + 1;
    if (opt == 1) add(l, r, x);
    if (opt == 2) assign(l, r, x);
    if (opt == 3) cout << rnk(l, r, x) << '\n';
    if (opt == 4) cout << getsum(l, r, x, y) << '\n';
  }
  return 0;
}

CF1423G Growing flowers

看到区间推平,果断珂朵莉。

对于一段长为 \(x\) 的极长连续段,对于答案的贡献是 \(\max(0, x - k + 1)\),线段树维护即可。

posted @ 2025-02-05 10:00  ydq1101  阅读(46)  评论(0)    收藏  举报