[DS记录] 啥都可能有的 DS 复习

1.莫队

1.1 回滚莫队

[Cnoi2019] 数字游戏

\([x, y]\) 固定,考虑 \(b_i = [x\le a_i \le y]\)。答案就是 \([l, r]\) 中每一段极长连续 \(1\)\(\sum \dbinom{len + 1}{2}\)

做法是值域上莫队,然后因为是排列,每次一个值域只会修改一个位置的值。考虑 \(0\to 1\) 是好处理的。

维护两个链表 \(pre, suf\),表示极长的包含 \(i\) 的段的开头和结尾。

对于序列分块,记录每个块,不包含开头末尾极长连续段贡献的其他贡献 mid。(如果整个串都是 1, mid[i] = 0)

pre 和 suf 的维护就是每次合并 \(p\) 两端的,然后比较繁琐。

\(1 \to 0\) 非常难,于是考虑回滚莫队撤销怎么做,我们考虑当进行别的操作时,用另一个数组继承原数组的值,然后操作完了,再标记回来,这样做,就不用考虑撤销的问题了。非常牛逼的 std 实现。

点击查看代码
// 僕らタイムフライヤー \
   時を駆け上がるクライマー \
   時のかくれんぼ \
   はぐれっこはもういやなんだ
#include <bits/stdc++.h>
#include <assert.h>
using namespace std;
typedef long long ll;
typedef double db;
#define int long long 
#define ep emplace_back
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define fout freopen("out.out","w",stdout);
#define fin freopen("in.in","r",stdin);
#define dd(x) cerr << #x" = " << x << endl;
inline int read() {
	int x=0, v=1,ch=getchar();
	while('0'>ch||ch>'9') {
		if(ch=='-') v=0;
		ch=getchar();
	}while('0'<=ch&&ch<='9') {
		x=(x*10)+(ch^'0');
		ch=getchar();
	}return v?x:-x;
}
const int MAX = 2e5 + 5;

int bl[MAX], L[MAX], R[MAX];
ll bm(int x) { return 1ll * x * (x + 1) / 2; }
template <const int SIZ> 
struct point {
	int rea[SIZ];
	int vit[SIZ]; 
	int t[SIZ];
	int cnt = 1;
	bool fvit = 0;
	
	void Roll() { ++ cnt; }
	void Vit() {fvit = 1, Roll(); }
	void Rea() {fvit = 0; }
	void Clr() { memset(rea, 0, sizeof(rea)); Roll(); }
	
	int &operator [] (int i) {
		if(fvit) {
			if(cnt ^ t[i]) t[i] = cnt, vit[i] = rea[i];
			return vit[i];
		}
		return rea[i];
	}
};

struct Q{
	int l, r, x, y, id;
	Q() {}
	Q(int _id) {
		l = read(), r = read(), x = read(), y = read();
		id = _id;
	}
	bool operator < (const Q & h) const {
		return (bl[x] == bl[h.x] ? y < h.y : x < h.x);
	}
} q[MAX];

int n, T;
int c[MAX];
int pc[MAX];

ll ret[MAX];

point<MAX> pre, nxt, mid;

int bf1(const Q & q) {
	int ans = 0, cnt = 0;
	for(int i = q.l; i <= q.r; ++ i) {
		if(q.x <= c[i] && c[i] <= q.y) {
			++ cnt;
		}else {
//			ans += bm(cnt);
			cnt = 0;
		}
		ans += cnt;
	}
	return ans;
}

int bf2(const Q & q) {
	int ans = 0;
	for(int i = q.x; i <= q.y; ++ i) {
		int p = pc[i];
		if(q.l <= p && p <= q.r) {
			ans -= bm(p - pre[p - 1]);
			ans -= bm(nxt[p + 1] - p);
			nxt[pre[p - 1]] = nxt[p + 1];
			pre[nxt[p + 1]] = pre[p - 1];
			ans += bm(nxt[p + 1] - pre[p - 1] + 1);
		}
	}
	pre.Roll(), nxt.Roll(); 
	return ans;
}

void add(int x) {
	/*
	
	修改时有原则:如果 p 不是块的左右端点,那么以后是不会再被用到的。 
	
	*/
	int p = pc[x], b = bl[p];
	if(p != L[b]) {
		if(pre[p - 1] != L[b]) mid[b] -= bm(p - pre[p - 1]);
		if(p == R[b]) {
			nxt[p] = p;
			pre[p] = pre[p - 1];
			nxt[pre[p - 1]] = p;
		}else {
			nxt[pre[p - 1]] = nxt[p + 1];
			pre[nxt[p + 1]] = pre[p - 1];
		}
	}
	if(p != R[b]) {
		if(nxt[p + 1] != R[b]) mid[b] -= bm(nxt[p + 1] - p);
		if(p == L[b]) {
			pre[p] = p;
			nxt[p] = nxt[p + 1];
			pre[nxt[p + 1]] = p;
		}else {
			nxt[pre[p - 1]] = nxt[p + 1];
			pre[nxt[p + 1]] = pre[p - 1];
		}
	}
	if(p != L[b] && p != R[b] && pre[p - 1] != L[b] && nxt[p + 1] != R[b]) {
		mid[b] += bm(nxt[p + 1] - pre[p - 1] + 1);
	}
}


signed main() {
	n = read(); T = read();
	for(int i = 1; i <= n; ++ i) {
		c[i] = read(); 
		pc[c[i]] = i; 
	}
	int B = n / (sqrt(T * 2 / 3) + 1) + 1; 
	int LIM = (n - 1) / B + 1;
	for(int i = 1; i <= LIM; ++ i) {
		L[i] = R[i - 1] + 1;
		R[i] = R[i - 1] + B; if(R[i] > n) R[i] = n;
		for(int j = L[i]; j <= R[i]; ++ j) bl[j] = i;
	} 
	
	for(int i = 0; i <= n + 1; ++ i) pre[i] = i + 1, nxt[i] = i - 1;
	nxt.Vit(), pre.Vit(), mid.Vit();
	
	int ec = 0;
	for(int i = 1; i <= T; ++ i) {
		Q x = Q(i);
		if(x.r - x.l <= B * 2) ret[i] = bf1(x);
		else if(x.y - x.x <= B * 2) ret[i] = bf2(x);
		else q[++ ec] = x;
	}	
	sort(q + 1, q + 1 + ec);
	int pt = 1;
	for(int t = 1; t < bl[n]; ++ t) {
		int r = R[t];
		nxt.Rea(), pre.Rea(), mid.Rea(); 
		for(int i = 0; i <= n + 1; ++ i) pre[i] = i + 1, nxt[i] = i - 1;
		mid.Clr();
		while(pt <= ec && bl[q[pt].x] == t) {
			const Q & nq = q[pt];
			nxt.Rea(), pre.Rea(), mid.Rea();
			while(r < nq.y) add(++ r);
			
			nxt.Vit(), pre.Vit(), mid.Vit();
			for(int j = nq.x; j <= R[t]; ++ j) 
				add(j);
			
			ll ans = 0, cnt = 0;
			for(int i = nq.l; i <= R[bl[nq.l]]; ++ i) {
				if(nq.x <= c[i] && c[i] <= nq.y) ++ cnt;
				else ans += bm(cnt), cnt = 0;
			}
			for(int i = bl[nq.l] + 1; i < bl[nq.r]; ++ i) {
				if(nxt[L[i]] == R[i]) cnt += R[i] - L[i] + 1; 
				else {
					cnt += nxt[L[i]] - L[i] + 1;
					ans += bm(cnt) + mid[i];
					cnt = R[i] - pre[R[i]] + 1;
				}
			}
			for(int i = L[bl[nq.r]]; i <= nq.r; ++ i) {
				if(nq.x <= c[i] && c[i] <= nq.y) ++ cnt;
				else ans += bm(cnt), cnt = 0;
			}
			
			ans += bm(cnt);
			ret[nq.id] = ans;
			
			nxt.Roll(), pre.Roll(), mid.Roll();
			++ pt; 
		}
	}
	for(int i = 1; i <= T; ++ i) printf("%lld\n", ret[i]);
	return 0;
}

1.2 待修莫队

1.3 树上莫队

posted @ 2023-03-13 22:49  Lates  阅读(32)  评论(0编辑  收藏  举报