Loading

第十分块

好不容易卡过去了,纪念一下。 link

可离线,很容易想到莫队。带修不好维护,考虑 \([l,r]\) 用 ds 维护,莫队扫时间和值域这两维就好了。

所以我们需要支持维护一个 \(\mathtt {01}\) 序列,动态修改某个位置上的数,并求出所有极长 \(\mathtt 1\) 的段的长度平方和。

注意一般情况时间是必须带 \(\log\) 的,可以尝试只支持 \(\mathtt 0\) 变成 \(\mathtt 1\) 或者撤销最近一次操作,这契合回滚莫队的机制。

按时间分块,同一块内,所有询问值域从小到大扫描,每次询问前需要额外加入 \(\mathcal O(\sqrt n)\) 次单点修改。

可以求出当前时间块的修改的所有位置上的历史最大值,先按照历史最大值的顺序加入。在每个询问前,再求出这些位置上的真实值,这样的确只有 \(\mathtt 0\) 变为 \(\mathtt 1\) 的修改。

维护 \(\mathtt {01}\) 序列中每个极长 \(\mathtt 1\) 段的左端点和右端点位置的匹配,即可快速找到一个左端点对应的右端点 / 一个右端点对应的左端点。

撤销是容易的,时间复杂度 \(\mathcal O((n + m) \sqrt n)\)

点击查看代码
#include <bits/stdc++.h>

namespace Initial {
	#define ll int
	#define ull unsigned long long
	#define fi first
	#define se second
	#define mkp make_pair
	#define pir pair <int, int>
	#define pb push_back
	#define i128 __int128
	using namespace std;
	const ll maxn = 3e5 + 10, inf = 2e9, mod = 1e9 + 7;
	ll power(ll a, ll b = mod - 2) {
		ll s = 1;
		while(b) {
			if(b & 1) s = s * a %mod;
			a = a * a %mod, b >>= 1;
		} return s;
	}
	template <class T>
	const inline ll pls(const T x, const T y) { return x + y >= mod? x + y - mod : x + y; }
	template <class T>
	const inline void add(T &x, const T y) { x = x + y >= mod? x + y - mod : x + y; }
	template <class T>
	const inline void chkmax(T &x, const T y) { x = x < y? y : x; }
	template <class T>
	const inline void chkmin(T &x, const T y) { x = x > y? y : x; }
} using namespace Initial;

namespace Read {
	char buf[1 << 22], *p1, *p2;
//	#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, (1 << 22) - 10, stdin), p1 == p2)? EOF : *p1++)
	template <class T>
	const inline void rd(T &x) {
		char ch; bool neg = 0;
		while(!isdigit(ch = getchar()))
			if(ch == '-') neg = 1;
		x = ch - '0';
		while(isdigit(ch = getchar()))
			x = (x << 1) + (x << 3) + ch - '0';
		if(neg) x = -x;
	}
} using Read::rd;

ll n, m, a[maxn]; long long ans[maxn];
struct Mdf { ll x, y; }; vector <Mdf> mdf;
struct Qry { ll ti, l, r, x, id; }; vector <Qry> qry;

ll max_val[maxn], now, h[maxn], ht;

ll nxt[maxn], pos[maxn], hd[maxn], tot;
void ins(ll x, ll y) {
	pos[++tot] = y, nxt[tot] = hd[x];
	hd[x] = tot;
}

namespace DS {
	ll st[maxn], stk[maxn], top, B, bl[maxn];
	ll res[maxn]; short cnt[maxn]; bool cov[maxn];
	void add(const ll x) {
		const ll u = bl[x]; ll l = 0, r = 0;
		++cnt[u], cov[x] = true, stk[++top] = x;
		if(bl[x - 1] == u) l = st[x - 1];
		if(bl[x + 1] == u) r = st[x + 1];
		if(!l) l = x; if(!r) r = x;
		st[l] = r, st[r] = l;
		const ll A = x - l, B = r - x, C = A + B + 1;
		res[u] += C * C - A * A - B * B;
	}
	void restore() {
		const ll x = stk[top--], u = bl[x]; ll l = 0, r = 0;
		--cnt[u], cov[x] = false;
		if(bl[x - 1] == u && cov[x - 1])
			st[l = st[x - 1] < x? st[x - 1] : x - 1] = x - 1;
		else l = x;
		if(bl[x + 1] == u && cov[x + 1])
			st[r = st[x + 1] > x? st[x + 1] : x + 1] = x + 1;
		else r = x;
		st[x] = 0;
		const ll A = x - l, B = r - x, C = A + B + 1;
		res[u] -= C * C - A * A - B * B;
	}
} using namespace DS; ll bin[maxn];

ll l, r;
long long ret; ll sum, cc;
void work(const ll u) {
	ll ul = (u - 1) * B + 1, ur = u * B;
	if(l <= ul && ur <= r) {
		cc += cnt[u];
		if(cnt[u] == ur - ul + 1) sum += ur - ul + 1;
		else {
			if(st[ul]) {
				sum += st[ul] - ul + 1;
				ret -= (st[ul] - ul + 1) * (st[ul] - ul + 1);
			}
			ret += 1ll * sum * sum + res[u], sum = 0;
			if(st[ur]) {
				sum = ur - st[ur] + 1;
				ret -= sum * sum;
			} 
		}
	} else {
		chkmax(ul, l), chkmin(ur, r);
		for(ll i = ul; i <= ur; i++)
			if(cov[i]) ++sum, ++cc;
			else ret += 1ll * sum * sum, sum = 0;
	}
}
const long long getans() {
	ret = sum = cc = 0;
	for(register ll u = bl[l]; u <= bl[r]; u++) work(u);
	return ret + 1ll * sum * sum + cc;
}

bool cmp(const Qry a, const Qry b) { return a.x < b.x; }

void solve(const ll L, const ll R) {
	memset(hd, 0, sizeof hd), tot = 0;
	for(register ll i = 1; i <= n; i++) max_val[i] = a[i];
	ht = 0;
	for(register ll i = L; i < R; i++)
		h[++ht] = mdf[i].x, chkmax(max_val[mdf[i].x], mdf[i].y);
	for(register ll i = 1; i <= n; i++) ins(max_val[i], i);
	stable_sort(h + 1, h + 1 + ht);
	ht = unique(h + 1, h + 1 + ht) - h - 1;
	ll to = now;
	while(to < qry.size() && qry[to].ti <= R) ++to;
	stable_sort(qry.begin() + now, qry.begin() + to, cmp);
	for(register ll i = now, j = 1; i < to; i++) {
		Qry Q = qry[i];
		while(j <= Q.x)
			for(ll k = hd[j++]; k; k = nxt[k])
				add(pos[k]);
		const ll tt = top;
		for(register ll k = Q.ti - 1; k >= L; k--)
			if(bin[mdf[k].x] <= i) {
				bin[mdf[k].x] = i + 1;
				if(max_val[mdf[k].x] > Q.x && mdf[k].y <= Q.x)
					add(mdf[k].x);
			}
		for(register ll k = 1; k <= ht; k++)
			if(bin[h[k]] <= i && max_val[h[k]] > Q.x
			 && a[h[k]] <= Q.x) add(h[k]);
		l = Q.l, r = Q.r, ans[Q.id] = getans();
		while(top > tt) restore();
	} now = to, top = 0;
	memset(st, 0, sizeof st);
	memset(cov, 0, sizeof cov);
	for(ll i = 1; i <= bl[n]; i++) cnt[i] = res[i] = 0;
}

signed main() {
	rd(n), rd(m); B = max(1, (ll) pow(n * 3 / 4, 0.5));
	for(register ll i = 1; i <= n; i++) rd(a[i]), bl[i] = (i - 1) / B + 1;
	for(register ll i = 1; i <= m; i++) {
		ll op; rd(op);
		if(op == 1) {
			Mdf tmp; rd(tmp.x), rd(tmp.y);
			mdf.pb(tmp); ans[i] = -1;
		} else {
			Qry tmp; rd(tmp.l), rd(tmp.r), rd(tmp.x);
			tmp.id = i, tmp.ti = mdf.size(), qry.pb(tmp);
		}
	}
	ll mdf_B = max(1, (ll) pow(mdf.size(), 0.58)), ul = 0, ur;
	while(1) {
		ur = min(ul + mdf_B, (ll) mdf.size() + 1);
		solve(ul, ur - 1);
		if(ur > mdf.size()) break;
		for(ll i = ul; i < ur; i++) a[mdf[i].x] = mdf[i].y;
		ul = ur;
	}
	for(register ll i = 1; i <= m; i++)
		if(ans[i] >= 0) printf("%lld\n", ans[i] >> 1);
	return 0;
}
posted @ 2024-12-13 19:15  Sktn0089  阅读(24)  评论(0)    收藏  举报