查区间
题目链接:YBT2023寒假Day7 C
题目大意
给你一个序列,要你维护两种操作,区间取 min 和区间求第 k 小。
思路
关于区间求第 k 小,有一个方法,是树套树。
外层线段树维护位置,内层线段树是值域线段树维护每个数的出现次数。
就可以做到 \(O(n\log^2n)\) 维护。
考虑区间取 \(\min\) 怎么搞。
发现你定位到每个线段树上的区间,有两个东西要考虑:
祖先的位置你要搞,子树的位置你也要改。
不过我们先看自己怎么改,就是可以把大于的所有数找出来,一个一个加进去(这样搞是因为易于操作,容易扩展)。
这样的复杂度是没问题的因为每次取 \(\min\) 不会使得总数的种类增加,而且你找了 \(x\) 个,种类就少了 \(x-1/x\)(可能放进去的值已经有了),那这样复杂度是 \(O(n)\) 的。
首先看简单的子树,那一个想法是懒标记,会发现确实可行,你就每个儿子都像你一样操作一下就好了。
接着是祖先,因为是线段树,所以至多 \(\log\) 个祖先,所以我们也可以尝试暴力改。
不过要注意的是你要用你一开始的区间里面找到的种类和数量,因为祖先区间里面有一些 \(>x\) 的不能被取 \(\min\)。
那这个复杂度就是 \(n\log ^3n\) 了。
(好像说是有一个 \(n\sqrt{n}\) 的分块套分块,外层是按位置分块,内层按值域分块,具体操作阿巴阿巴不会捏)
代码
#include<cstdio>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 8e4 + 100;
int n, m, a[N], b[N];
vector <pair<int, int> > tmp;
vector <int> Tmp;
struct Make_XD_tree {
int tot, f[N << 9], ls[N << 9], rs[N << 9];
int new_point() {
return ++tot;
}
void up(int now) {
f[now] = f[ls[now]] + f[rs[now]];
}
void add(int &now, int l, int r, int pl, int x) {
if (!now) now = new_point();
if (l == r) {
f[now] += x;
return ;
}
int mid = (l + r) >> 1;
if (pl <= mid) add(ls[now], l, mid, pl, x);
else add(rs[now], mid + 1, r, pl, x);
up(now);
}
vector <int> Left(vector <int> now) {
vector <int> op; op.clear();
for (int i = 0; i < now.size(); i++) if (ls[now[i]]) op.push_back(ls[now[i]]);
return op;
}
vector <int> Right(vector <int> now) {
vector <int> op; op.clear();
for (int i = 0; i < now.size(); i++) if (rs[now[i]]) op.push_back(rs[now[i]]);
return op;
}
int getk(vector <int> now, int l, int r, int k) {
if (l == r) return l;
int mid = (l + r) >> 1, cnt = 0;
for (int i = 0; i < now.size(); i++) if (ls[now[i]]) cnt += f[ls[now[i]]];
if (cnt >= k) return getk(Left(now), l, mid, k);
else return getk(Right(now), mid + 1, r, k - cnt);
}
void ask(int now, int l, int r, int L, int R) {
if (L > R) return ;
if (!f[now]) return ;
if (!now) return ;
if (l == r) {
tmp.push_back(make_pair(l, f[now])); return ;
}
int mid = (l + r) >> 1;
if (L <= mid) ask(ls[now], l, mid, L, R);
if (mid < R) ask(rs[now], mid + 1, r, L, R);
}
}Tr;
struct XD_Tree {
int lzy[N << 3], rt[N << 3];
void build(int now, int l, int r) {
lzy[now] = 2000000000;
for (int i = l; i <= r; i++) {
Tr.add(rt[now], 1, n, a[i], 1);
}
if (l == r) return ;
int mid = (l + r) >> 1;
build(now << 1, l, mid); build(now << 1 | 1, mid + 1, r);
}
void downc(int now, int x) {
lzy[now] = min(lzy[now], x);
tmp.clear(); Tr.ask(rt[now], 1, n, x + 1, n);
int cnt = 0;
for (int i = 0; i < tmp.size(); i++) {
Tr.add(rt[now], 1, n, tmp[i].first, -tmp[i].second);
cnt += tmp[i].second;
}
Tr.add(rt[now], 1, n, x, cnt);
}
void down(int now) {
if (lzy[now] != 2000000000) {
downc(now << 1, lzy[now]); downc(now << 1 | 1, lzy[now]);
lzy[now] = 2000000000;
}
}
void ask(int now, int l, int r, int L, int R) {
if (L <= l && r <= R) {
Tmp.push_back(rt[now]); return ;
}
int mid = (l + r) >> 1; down(now);
if (L <= mid) ask(now << 1, l, mid, L, R);
if (mid < R) ask(now << 1 | 1, mid + 1, r, L, R);
}
void update(int now, int l, int r, int L, int R, int x) {
if (L <= l && r <= R) {
lzy[now] = min(lzy[now], x);
tmp.clear(); Tr.ask(rt[now], 1, n, x + 1, n);
int Now = now, cnt = 0;
for (int i = 0; i < tmp.size(); i++) cnt += tmp[i].second;
while (Now) {
for (int i = 0; i < tmp.size(); i++) Tr.add(rt[Now], 1, n, tmp[i].first, -tmp[i].second);
Tr.add(rt[Now], 1, n, x, cnt);
Now >>= 1;
}
return ;
}
int mid = (l + r) >> 1; down(now);
if (L <= mid) update(now << 1, l, mid, L, R, x);
if (mid < R) update(now << 1 | 1, mid + 1, r, L, R, x);
}
}T;
int re; char c;
int read() {
c = getchar(); re = 0;
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {
re = (re << 3) + (re << 1) + c - '0';
c = getchar();
}
return re;
}
int main() {
freopen("segment.in", "r", stdin);
freopen("segment.out", "w", stdout);
n = read(); m = read();
for (int i = 1; i <= n; i++) a[i] = read();
T.build(1, 1, n);
for (int i = 1; i <= m; i++) {
int op = read();
if (op == 1) {
int l = read(), r = read(), x = read();
if (x < n) T.update(1, 1, n, l, r, x);
}
if (op == 2) {
int l = read(), r = read(), x = read();
Tmp.clear(); T.ask(1, 1, n, l, r);
printf("%d\n", Tr.getk(Tmp, 1, n, x));
}
}
return 0;
}
标签:return,树套,int,Day7,线段,vector,now,op
From: https://www.cnblogs.com/Sakura-TJH/p/YBT2023Day7_C.html