分块算法主要用于给定序列的区间询问问题,能够以较小的时间代价暴力求解,时间复杂度一般在O(n*n^0.5)。关键在O(1)维护好某一区间在增加或者减少一个边界元素所带来的影响。需要注意的就是在更新的区间的时候要先放大在缩小,否则可能出现当前区间左右边界互换的情况,这个影响某一些题可能没有影响,但是极有可能出错。
时间复杂度:先考虑左边界的时间复杂度,由于分成了sqrt(n)块,而同一块中左标移动的范围最多是sqrt(n),那相邻块跳转的情况呢?可以虚拟出每块中有至少一个询问进行思考,那么相邻块之间的移动次数最大为2*sqrt(n)。由于共有Q次询问,因此最终时间复杂度为O(Q*sqrt(n))。再考虑右边界,对于同一块内的右边界来说,其值是单调递增的,因此时间复杂度为O(n),相邻块跳转为O(2*n),由于共有sqrt(n)块,因此最终时间复杂度为O(n*sqrt(n))。
CF86D Powerful array
题意:给定一N个元素的序列,序列长度最多为200000,现在定义若一个数s在区间[L, R]出现Ks次,那么这个串的价值就加上Ks^2*s。例如序列3,3,5,6,5,5,那么这个串的权值为:2^2*3+3^2*5+1^1*6。现有Q(与N同)次询问,每次询问都是[L, R],要出输出该子串的权值。
分析:离线处理,将所有的询问排序,第一关键字按照左端点所在块排序,第二关键字按照右端点排序。在确保能够O(1)在某一区间扩大/缩小一单位维护出答案的情况下,这样能够保证在O(Q*n^0.5+n*n^0.5)的时间复杂度计算出来。至于O(1)的处理,只需要考虑 Ks^2*s 和 (Ks-1)^2*s 的关系即可。
#include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; typedef long long LL; struct Node { int l, r, b, No; bool operator < (const Node &t) const { if (b != t.b) return b < t.b; return r < t.r; } }; const int N = 200005; const int M = 1000005; int n, Q; int seq[N]; Node q[N]; int cnt[M]; // 统计每个数出现了多少次,如果范围过大可以考虑离散化 LL ans[N]; LL & cal(int x, int &l, int &r, LL &ret) { int L = q[x].l, R = q[x].r; while (l > L) { --l; int t = seq[l]; ret += 2LL*cnt[t]*t+t; cnt[t]++; } while (r < R) { ++r; int t = seq[r]; ret += 2LL*cnt[t]*t+t; cnt[t]++; } while (l < L) { int t = seq[l]; ret += -2LL*cnt[t]*t+t; ++l; cnt[t]--; } while (r > R) { int t = seq[r]; ret += -2LL*cnt[t]*t+t; --r; cnt[t]--; } return ret; } void solve() { int B = (int)sqrt(1.0*n); memset(cnt, 0, sizeof (cnt)); for (int i = 1; i <= Q; ++i) { scanf("%d %d", &q[i].l, &q[i].r); q[i].b = q[i].l / B; q[i].No = i; } sort(q+1, q+1+Q); int l = q[1].l, r = q[1].r; LL ret = 0; for (int i = l; i <= r; ++i) { int t = seq[i]; ret += 2LL*cnt[t]*t+t; cnt[t]++; } ans[q[1].No] = ret; for (int i = 2; i <= Q; ++i) { ans[q[i].No] = cal(i, l, r, ret); } for (int i = 1; i <= Q; ++i) { printf("%I64d\n", ans[i]); } } int main() { while (scanf("%d %d", &n, &Q) != EOF) { for (int i = 1; i <= n; ++i) { scanf("%d", &seq[i]); } solve(); } return 0; }