Y
K
N
U
F

[忘发了]P3710 方方方的数据结构

我!卡!了!一!晚!上!常!

真是有趣极了,发现和题解区现有的卡常方式不同,为缓解心中不适,故记录。

正文

这段不难,略讲。

没有删除操作的话这题就可以直接线段树维护,因为有删除,所以考虑处理删除。

一个比较容易的方法就是直接暴力重构所有操作,但是显然不行。

考虑对着操作序列分块,处理出某位置上的一个数字 \(x\) 经过这一块后会变成的 \(ax+b\)\(a\)\(b\)

你发现这是很简单的线段树操作,而且分块维护可以保证查询和重构的复杂度都是 \(\sqrt m \log n\) 的。

然后就完了。

接下来都是重要的卡常技巧。

卡常

先来两句和写法无关的卡常方式。

老生常谈,快读,调块长,不要 #define int long long

以及用 uint 代替 int,这样据说取模会快点。

然后是写法方面。

首先可以懒重构,到下一次查询的时候再重构,会快很多。

然后是发现既然要重构,那么从有删除开始就别再修改了,等着重构就行,会快很多。

以及很重要的是要离散化,逐块离散化,会快很多。同时对着 \(l,r,l-1,r+1\) 离散化就可以避免出现多查漏查,这样处理可以少很多分讨。

然后另一个比较重要的点是,线段树你只需要维护懒标记就行了,不用维护 \(a\)\(b\) 具体的值,因为它是单点查询,而对于单点,也就是线段树底层,懒标记和它的值是等价的。

以及把暴力回收节点的重构改成用懒标记清空不知道会不会快,总之我知道会更难写。

代码说是:

// code by 樓影沫瞬_Hz17
#include <bits/stdc++.h>
 
#define getc() getchar_unlocked()
#define putc(a) putchar_unlocked(a)
#define en_ putc('\n')
#define e_ putc(' ')
 
using std::cout;
using std::cerr;

template<class T> inline T in() { 
	T n = 0; char p = getc();
	while(p < '-') p = getc();
	bool f = p == '-' ? p = getc() : 0;
	do n = n * 10 + (p ^ 48), p = getc();
	while(isdigit(p));
	return f ? -n : n;
}
template<class T> inline T in(T &a) { return a = in<T>(); }
template<class T, class ... Args> inline void in(T &t, Args&... args) { in(t), in(args...); }
 
template<class T> inline void out(T n) {
	if(n < 0) putc('-'), n = -n;
	if(n > 9) out(n / 10);
	putc(n % 10 + '0');
}
 
template<class T1, class T2> T1 max(T1 a, T2 b) { return a > b ? a : a = b;}
template<class T1, class T2> T1 min(T1 a, T2 b) { return a < b ? a : a = b;}
 
constexpr uint N = 15e4 + 10, mod = 998244353, B = 450;

uint n, m;
std::vector<uint> x[N / B + 10];
struct val {
	uint times, plus;
	val() { times = 1; }
	val(uint i, uint j) { times = j, plus = i; }
	void init() { times = 1, plus = 0; }
} ;
struct node {
	val tag;
	uint l, r;
	node() {}
	void init() { l = r = 0, tag.init(); }
} ;
node t[N * 25];
uint pool[N * 25], tp;

struct Block {
	struct Tree {
		inline uint append() {
			uint u = pool[tp --];
			t[u].init();
			stk.emplace_back(u);
			return u;
		}
		std::vector<uint> stk;
		inline void down(uint u) {
			if(t[u].tag.times != 1) {
				if(!t[u].l) t[u].l = append();
				if(!t[u].r) t[u].r = append();
				t[t[u].l].tag.times = 1ull * t[t[u].l].tag.times * t[u].tag.times % mod;
				t[t[u].l].tag.plus = 1ull * t[t[u].l].tag.plus * t[u].tag.times % mod;
				t[t[u].r].tag.times = 1ull * t[t[u].r].tag.times * t[u].tag.times % mod;
				t[t[u].r].tag.plus = 1ull * t[t[u].r].tag.plus * t[u].tag.times % mod;
				t[u].tag.times = 1; 
			}
			if(t[u].tag.plus) {	
				if(!t[u].l) t[u].l = append();												   
				if(!t[u].r) t[u].r = append();													
				t[t[u].l].tag.plus = (t[t[u].l].tag.plus + t[u].tag.plus) % mod;
				t[t[u].r].tag.plus = (t[t[u].r].tag.plus + t[u].tag.plus) % mod;
				t[u].tag.plus = 0;
			}
		}
		#define mid ((l + r) >> 1)
		inline void updatePlus(uint&u, uint l, uint r, uint L, uint R, uint add) {
			if(!u) u = append();
			if(L <= l and r <= R) return void(t[u].tag.plus = (add + t[u].tag.plus) % mod);
			down(u);
			if(L <= mid) updatePlus(t[u].l, l, mid, L, R, add);
			if(R > mid) updatePlus(t[u].r, mid + 1, r, L, R, add);
		}
		inline void updateTimes(uint&u, uint l, uint r, uint L, uint R, uint add) {
			if(!u) u = append();
			if(L <= l and r <= R) return void((t[u].tag.plus = 1ull * t[u].tag.plus * add % mod) | (t[u].tag.times = 1ull * t[u].tag.times * add % mod));
			down(u);
			if(L <= mid) updateTimes(t[u].l, l, mid, L, R, add);
			if(R > mid) updateTimes(t[u].r, mid + 1, r, L, R, add);
		}
		inline val query(uint u, uint l, uint r, uint p) {
			if(!u) return {0, 1};
			if(l == r) return t[u].tag;
			down(u);
			if(p <= mid) return query(t[u].l, l, mid, p);
			return query(t[u].r, mid + 1, r, p);
		}
		#undef mid
		inline void clear() { 
			for(uint v : stk) pool[++ tp] = v;
			stk.clear();
		}
	} ;
	uint neq;
	Tree tr; uint rt;
	struct Calc {
		uint l, r, op, d, id;
	} ;
	std::vector<Calc> calc;
	using iter = std::vector<Calc>::iterator;
	bool changed;
	inline void Rebuild() {
		tr.clear(), rt = 0;
		for(Calc it : calc) {
			if(it.op == 1) tr.updatePlus(rt, 0, neq, it.l, it.r, it.d);
			if(it.op == 2) tr.updateTimes(rt, 0, neq, it.l, it.r, it.d);
		}
	}
	inline void Erase(uint id) {
		changed = 1;
		for(iter it = calc.begin(); it != calc.end(); it ++) 
			if(it->id == id) {
				calc.erase(it);
				break;
			}
	}
	inline void Insert(Calc it) {
		calc.emplace_back(it);
		if(changed) return;
		if(it.op == 1) tr.updatePlus(rt, 0, neq, it.l, it.r, it.d);
		if(it.op == 2) tr.updateTimes(rt, 0, neq, it.l, it.r, it.d);
	}
	inline void Query(uint&x, uint pos) {
		if(changed) {
			changed = 0;
			Rebuild();
		}
		val end = tr.query(rt, 0, neq, pos);
		x = (1ull * x * end.times + end.plus) % mod;
	}

	Block() { changed = 0; rt = 0; }
} A[N / B + 10];

uint pos[N];
struct czm {
	uint op, l, r, d;
} Q[N];

uint L[N / B + 10], R[N / B + 10];

signed main() {
	#ifndef ONLINE_JUDGE
		freopen("in.ru", "r", stdin);
		freopen("out.ru", "w", stdout);
	#endif
	in(n, m);
	for(uint i = 1; i <= m * 25; i ++) pool[++ tp] = i;

	for(uint i = 1; R[i - 1] != m; i ++) {
		L[i] = (i - 1) * B + 1, R[i] = min(m, i * B);
		for(uint j = L[i]; j <= R[i]; j ++) pos[j] = i;
	}

	for(uint i = 1, op, l, r, d; i <= m; i ++) {
		in(op);
		if(op == 1 || op == 2) in(l, r, d);
		else in(d);
		Q[i] = {op, l, r, d};
 	}

	for(uint j = 1; j <= pos[m]; j ++) {
		for(uint i = L[j]; i <= R[j]; i ++) {
			if(Q[i].op == 1 || Q[i].op == 2) x[j].emplace_back(Q[i].l), x[j].emplace_back(Q[i].r),
										     x[j].emplace_back(Q[i].l - 1), x[j].emplace_back(Q[i].r + 1);
		}
		std::sort(x[j].begin(), x[j].end());
		x[j].erase(std::unique(x[j].begin(), x[j].end()), x[j].end());
		A[j].neq = x[j].size() - 1;
		for(uint i = L[j]; i <= R[j]; i ++) {
			if(Q[i].op == 1 || Q[i].op == 2) {
				Q[i].l = std::lower_bound(x[j].begin(), x[j].end(), Q[i].l) - x[j].begin();
				Q[i].r = std::lower_bound(x[j].begin(), x[j].end(), Q[i].r) - x[j].begin();
			}
		}
	}
	
	for(uint i = 1, op, l, r, d; i <= m; i ++) {
		op = Q[i].op;
		if(op == 1 || op == 2) {
			l = Q[i].l, r = Q[i].r, d = Q[i].d;
			A[pos[i]].Insert({l, r, op, d, i});
		}
		if(op == 3) {
			d = Q[i].d;
			uint ans = 0;
			for(uint j = 1; j <= pos[i]; j ++) 
				A[j].Query(ans, std::lower_bound(x[j].begin(), x[j].end(), d) - x[j].begin());
			out(ans), en_;
		}
		if(op == 4) {
			d = Q[i].d;
			A[pos[d]].Erase(d);
		}
	}
}
// 星間~ 干渉~ 融解~ 輪迴~ 邂逅~ 再生~ ララバイ~
posted @ 2025-11-14 10:34  樓影沫瞬_Hz17  阅读(12)  评论(0)    收藏  举报