[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
*/