bzoj2906 颜色 分块+块大小分析
题目传送门
https://lydsy.com/JudgeOnline/problem.php?id=2906
题解
如果可以离线的话,那么这个题目就是一个莫队的裸题。
看上去这个数据范围也还会一个根号算法,所以考虑分块。
每一次询问,我们需要知道整块的答案。如果单独知道整块看上去不太好搞,所以可以预处理整块到整块的答案。令 \(c[i][j][k]\) 表示从 \(i\) 块到 \(j\) 块 \(\leq k\) 的答案,询问的时候 \(ans\) 先加上中间整块的 \(c\) 值,然后枚举小块中的点,每个点都可以统计一次这个颜色在整块和之前统计过的部分的出现次数,然后减掉之前的,加上现在新的,就可以更新答案了。
考虑 \(c[i][j][k]\) 应该怎么求。令 \(b[i][k]\) 表示前 \(i\) 个块中出现的颜色 \(k\) 的方案数。前缀和减一下就好了。
但是如果直接按照 \(\sqrt n\) 分块的话,时空复杂度显然要炸。所以我们需要调整块大小。预处理的时空复杂度显然都是 \(O((\frac nb) ^ 2 m)\),后面的询问的复杂度是 \(O(qb)\)。当 \(b = n^{\frac 23}\) 时最优。
因此总的时间复杂度为 \(O(n^{\frac23} m + n^{\frac 23} q)\)。
#include
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back
template inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}
typedef long long ll; typedef unsigned long long ull; typedef std::pair pii;
template inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
const int N = 50000 + 7;
const int M = 20000 + 7;
const int B = 40 + 3;
#define bl(x) (((x) - 1) / blo + 1)
#define st(x) (((x) - 1) * blo + 1)
#define ed(x) std::min((x) * blo, n)
int n, m, Q, blo;
int a[N];
ll sqr[N], c[B][B][M];
int b[B][M], cnt[M], pp[M];
inline void ycl() {
for (int i = 1; i <= n; ++i) sqr[i] = (ll)i * i;
for (int i = 1; i <= bl(n); ++i) {
for (int j = st(i); j <= ed(i); ++j) ++b[i][a[j]];
// for (int j = 1; j <= m; ++j) b[i][j] += b[i][j - 1];
for (int j = 1; j <= m; ++j) b[i][j] += b[i - 1][j];//, dbg("b[%d][%d] = %d\n", i, j, b[i][j]);
}
// assert(b[bl(n)][m] == n);
for (int i = 1; i <= bl(n); ++i)
for (int j = i; j <= bl(n); ++j) {
for (int k = 1; k <= m; ++k) c[i][j][k] = sqr[b[j][k] - b[i - 1][k]];//, dbg("c[%d][%d][%d] = %d\n", i, j, k, c[i][j][k]);
for (int k = 1; k <= m; ++k) c[i][j][k] += c[i][j][k - 1];
}
}
inline ll qry(int l, int r, int k) {
if (k <= 0) return 0;
if (bl(l) == bl(r)) {
ll ans = 0;
for (int i = l; i <= r; ++i) if (a[i] <= k) ++cnt[a[i]], pp[a[i]] = i;
for (int i = l; i <= r; ++i) if (a[i] <= k && pp[a[i]] == i) ans += sqr[cnt[a[i]]], cnt[a[i]] = 0;
return ans;
}
ll ans = c[bl(l) + 1][bl(r) - 1][k];
// dbg("ans = %lld\n", ans);
for (int i = l; i <= ed(bl(l)); ++i) if (a[i] <= k) ++cnt[a[i]], pp[a[i]] = i;
for (int i = st(bl(r)); i <= r; ++i) if (a[i] <= k) ++cnt[a[i]], pp[a[i]] = i;
for (int i = l; i <= ed(bl(l)); ++i) if (a[i] <= k && pp[a[i]] == i) {
ans += sqr[cnt[a[i]] + b[bl(r) - 1][a[i]] - b[bl(l)][a[i]]];
ans -= sqr[b[bl(r) - 1][a[i]] - b[bl(l)][a[i]]];
cnt[a[i]] = 0;
}
for (int i = st(bl(r)); i <= r; ++i) if (a[i] <= k && pp[a[i]] == i) {
ans += sqr[cnt[a[i]] + b[bl(r) - 1][a[i]] - b[bl(l)][a[i]]];
ans -= sqr[b[bl(r) - 1][a[i]] - b[bl(l)][a[i]]];
cnt[a[i]] = 0;
}
return ans;
}
inline void work() {
ycl();
ll la = 0;
while (Q--) {
int l, r, a, b;
read(l), read(r), read(a), read(b);
l ^= la, r ^= la, a ^= la, b ^= la;
assert(l <= r);
assert(r <= n);
assert(l >= 1);
assert(a <= b);
assert(b <= m);
assert(a >= 1);
printf("%lld\n", la = qry(l, r, b) - qry(l, r, a - 1));
}
}
inline void init() {
read(n), read(m), read(Q);
blo = pow(n, 2.0 / 3);
// blo = ;
for (int i = 1; i <= n; ++i) read(a[i]);
// dbg("******* blo = %d\n", blo);
}
int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}