http://codeforces.com/contest/86/problem/D
莫队算法就是处理这类能O1从区间【l,r】得到 【l-1,r】【l,r+1】,不带修改的区间查询的一类问题。
题目要求任意 subarray 的 cnt[a[i]]*cnt[a[i]]*a[i]
按照套路,把所有区间按 左端点 所在块作第一关键字,右端点第二关键字 排序
遍历,预处理第一次询问后,每次询问暴力求解,转移如下:
res -= cnt[a[i]]*cnt[a[i]]*a[i];
cnt[a[i]]++;
res += cnt[a[i]]*cnt[a[i]]*a[i];
算法复杂度为q*sqrt n
考虑右指针,先是按照递增顺序移动,假设当前块右指针在最右端,当下一块时,需移动到最左端,那么也就是每跨越一个块,最多移动O(N),总共sqrtn个块,复杂度n*sqrtn
考虑左指针如果在同一块,移动量不超过sqrtn【在一块内嘛】
如果是跨越一块,移动量最多不超过2*sqrtn【块长只有sqrtn】
复杂度(q*sqrtn)
总复杂度(q+n)*sqrtn
----------------------------------------------------------
有一点 add move 的时候直接那刚才的暴力转移会超时,
因为每次变化只有1, 也就是 x*x*a 变成 (x+1)*(x+1)*a,显然我们直接加上 a*(2*x+1)就好了。。。最后跑了 1.9S
#include <cstdio> #include <cmath> #include <cstring> #include <string> #include <algorithm> #include <queue> #include <map> #include <set> #include <vector> #include <iostream> using namespace std; #define N 200100 typedef long long ll; int a[N], cnt[N*5]; ll ans[N], res; int L, R; struct node { int x, y, l, p; } q[N]; bool cmp(const node &x, const node &y) { if (x.l == y.l) return x.y < y.y; return x.l < y.l; } void query(int x, int y, int flag) { if (flag) { for (int i=x; i<L; i++) { res += (cnt[a[i]]*2+1)*a[i]; cnt[a[i]]++; } for (int i=L; i<x; i++) { res += (-cnt[a[i]]*2+1)*a[i]; cnt[a[i]]--; } for (int i=y+1; i<=R; i++) { res += (-cnt[a[i]]*2+1)*a[i]; cnt[a[i]]--; } for (int i=R+1; i<=y; i++) { res += (cnt[a[i]]*2+1)*a[i]; cnt[a[i]]++; } } else { for (int i=x; i<=y; i++) { res += (cnt[a[i]]*2+1)*a[i]; cnt[a[i]]++; } } L = x, R = y; } int main() { int n, t; scanf("%d%d", &n, &t); for (int i=1; i<=n; i++) scanf("%d", &a[i]); int block_size = sqrt(1.0*n); for (int i=0; i<t; i++) { scanf("%d%d", &q[i].x, &q[i].y); q[i].l = q[i].x / block_size; q[i].p = i; } sort(q, q+t, cmp); res = 0; for (int i=0; i<t; i++) { query(q[i].x, q[i].y, i); ans[q[i].p] = res; } for (int i=0; i<t; i++) printf("%I64d\n", ans[i]); return 0; }