首页 > 其他分享 >【YBT2023寒假Day7 C】查区间(线段树套线段树)

【YBT2023寒假Day7 C】查区间(线段树套线段树)

时间:2023-02-07 22:23:45浏览次数:69  
标签:return 树套 int Day7 线段 vector now op

查区间

题目链接: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

相关文章