[SNOI2022] 军队 题解

\(n \leqslant 2.5\times 10^5\)。疯狂暗示正解并不是 \(\mathcal {O}(\mathrm{nlog_2n})\)
考虑用并查集维护颜色的改变。
能否启发式合并?每次如果大的块上有懒标,就先将较小块的懒标提前减去再合并。
虽说挺妙,但是有区间 \([l,r]\) 的修改限制,我们就不知道哪些改哪些不改。
我们能不能将修改限制变为 “全局” 的呢?想到分块。
局部暴力搞,整块就用刚才那个方法。时间复杂度是 \(\mathcal {O}(n \sqrt n+n\ln\sqrt n)\)
但其实,不用启发式直接暴力合并也是 \(\mathcal {O}(n \sqrt n)\),合并的复杂度为 \(\sqrt n\times (\sqrt n)^2\)
感觉自己的这个做法比题解那个好写多了,但还是学习了一下题解的做法,写了一下,好 tm 难写。。
题解的做法是合并的时候建一颗树,在上面打懒标,重构的时候就传下来。
注意 bt 表示的是 初始 颜色为 \(i\) 的节点在最底部的哪个点。所以当一个颜色变为另一个颜色的时候不能修改 bt!!

点击查看代码
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#define uint unsigned int
#define LL long long
#define mr make_pair
#define pr pair <int, int>
#define fr first
#define se second
using namespace std;
const int MAXN = 5 * 100000 + 5;
int n, q, C, c[MAXN], col[MAXN], re[MAXN], wh[MAXN], nk[MAXN];
int op[MAXN], L[MAXN], R[MAXN], X[MAXN], Y[MAXN], t, cnt, fa[MAXN], tp[MAXN], ct[MAXN], bt[MAXN];
LL res[MAXN], add[MAXN], all, a[MAXN];
// 每个块可以独立计算 
/*
fa:树上节点的父亲
tp:第i个颜色现在由哪个代表 
res:第 i个询问的答案
add:树上节点的懒标 
ct:树上节点的 size 
*/ 
void rebuild(int l, int r) {
	for(int i = 1; i <= cnt; i ++) ct[i] = 0, tp[nk[i]] = 0;
	cnt = 0; all = 0;
	for(int i = l; i <= r; i ++) bt[c[i]] = 0;
	for(int i = l; i <= r; i ++) {
		if(!bt[c[i]]) bt[c[i]] = ++ cnt, nk[cnt] = c[i], add[cnt] = 0, tp[c[i]] = cnt, fa[cnt] = 0, ct[cnt] = 0;
		ct[bt[c[i]]] ++; all += a[i];
	}
}
int main() {
	freopen("military4.in", "r", stdin);
	freopen("military4.out", "w", stdout);
	scanf("%d%d%d", &n, &q, &C); t = sqrt(n); cnt = n;
	for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
	for(int i = 1; i <= n; i ++) scanf("%d", &c[i]);
	for(int i = 1; i <= q; i ++) {
		scanf("%d%d%d", &op[i], &L[i], &R[i]);
		if(op[i] <= 2) scanf("%d%d", &X[i], &Y[i]);
	}
	int m;
	for(int l = 1, r; l <= n; l = r + 1) {
		r = l + t - 1; m = 0;
		rebuild(l, r);
		// 记得用 ct 判断 ! 
		for(int i = 1; i <= q; i ++) {
			if(op[i] == 3) m ++;
			if(l >= L[i] && r <= R[i]) {
				if(op[i] == 1) {
					int u = tp[X[i]], v = tp[Y[i]];
//					printf("|%d %d %d %d|>>\n", u,v,ct[u], ct[v]);
					if(!u || ct[u] == 0) continue;
					if(!v || ct[v] == 0) {
//						bt[Y[i]] = tp[X[i]]; 
						tp[Y[i]] = tp[X[i]]; tp[X[i]] = 0; nk[tp[Y[i]]] = Y[i]; continue;
					}
					cnt ++; add[cnt] = 0; ct[cnt] = ct[v] + ct[u]; fa[u] = cnt; fa[v] = cnt;
					tp[Y[i]] = cnt; tp[X[i]] = 0; nk[cnt] = Y[i]; fa[cnt] = 0;
				}
				else if(op[i] == 2) {
					if(!tp[X[i]]) continue; 
					all += 1ll * Y[i] * ct[tp[X[i]]]; add[tp[X[i]]] += Y[i];
//					printf("|%d %d %d %d|\n", all, Y[i], ct[tp[X[i]]], i);
				}
				else res[m] += all;//, printf("qry|%lld %d|\n", all,m);
			}
			else if(r < L[i] || l > R[i]) continue;
			else {
				for(int j = cnt; j >= 1; j --) if(fa[j]) add[j] += add[fa[j]], nk[j] = nk[fa[j]];
				for(int j = l; j <= r; j ++) a[j] += add[bt[c[j]]];
				for(int j = l; j <= r; j ++) c[j] = nk[bt[c[j]]];
				if(op[i] == 1) { 
					for(int j = l; j <= r; j ++) if(j >= L[i] && j <= R[i] && c[j] == X[i]) c[j] = Y[i];
				}
				else if(op[i] == 2) {
					for(int j = l; j <= r; j ++) if(j >= L[i] && j <= R[i] && c[j] == X[i]) a[j] += Y[i];
				}
				else {
					LL qwq = 0;
					for(int j = l; j <= r; j ++) if(j >= L[i] && j <= R[i]) qwq += a[j];
					res[m] += qwq;
				}
				rebuild(l, r);
			} 
		}
	}
	for(int i = 1; i <= m; i ++) printf("%lld\n", res[i]);
	return 0;
}
/*
2 3 2
1 2
1 2
1 1 1 1 2
2 1 2 2 2
3 1 2
*/
posted @ 2022-07-20 14:31  Saintex  阅读(102)  评论(0编辑  收藏  举报