莫队算法
http://www.cnblogs.com/hzf-sbit/p/4056874.html
https://www.zhihu.com/question/27316467/answer/36260465
处理一类无修改的离线区间询问问题
复杂度为O(n*sqrt(n)*a),a为单次更新操作的复杂度。
#include<bits/stdc++.h> using namespace std; const int N = 2e4+20; int a[N]; long long ans[N], now; struct Q{ int l, r, p; }q[N]; int c_num[N], c_time[N]; pair<long long, long long> sum(int x) { long long num = 0, time = 0; while(x) { num += c_num[x]; time += c_time[x]; x -= x&-x; } return {num, time}; } void add(int x, int d) { for(int i = x; i < N; i += i&-i) { c_num[i] += d; c_time[i] += d > 0? x: -x; } } void add(int x) { x = a[x]; auto u = sum(x), v = sum(N-1); now += (v.first-u.first)*x+u.second+x; add(x, 1); } void del(int x) { x = a[x]; add(x, -1); auto u = sum(x), v = sum(N-1); now -= (v.first-u.first)*x+u.second+x; } int main() { int t; scanf("%d", &t); while(t--) { memset(c_num, 0, sizeof(c_num)); memset(c_time, 0, sizeof(c_time)); now = 0; int n, m; scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) scanf("%d", a+i); /*莫队算法*/ int lim = sqrt(n+0.5); for(int l, r, i = 1; i <= m; i++) { scanf("%d%d", &l, &r); q[i] = Q{l, r, i}; } sort(q+1, q+m+1, [&](Q a, Q b){ return a.l/lim == b.l/lim? a.r < b.r : a.l/lim < b.l/lim; }); for(int i = 1, l = 1, r = 0; i <= m; i++) { int L = q[i].l, R = q[i].r; while(l < L) del(l++); while(l > L) add(--l); while(r < R) add(++r); while(r > R) del(r--); ans[ q[i].p ] = now; } for(int i = 1; i <= m; i++) printf("%lld\n", ans[i]); } return 0; }
作者:张瑯小强 链接:https://www.zhihu.com/question/27316467/answer/130423804 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 ********************************* 问题:有n个数组成一个序列,有m个形如询问L, R的询问,每次询问需要回答区间内至少出现2次的数有哪些。 ********************************* int len; // 块长度 struct Query{ int L, R, ID, block; Query(){} // 构造函数重载 Query(int l, int r, int ID):L(l), R(r), ID(ID){ block = l / len; } bool operator < (const Query rhs) const { if(block == rhs.block) return R < rhs.R; // 不是if(L == rhs.L) return R < rhs.R; return L < rhs.L return block < rhs.block; // 否则这就变回算法一了 } }queries[maxm]; map<int, int> buf; inline void insert(int n){ if(buf.count(n)) ++buf[n]; else buf[n] = 1; } inline void erase(int n){ if(--buf[n] == 0) buf.erase(n); } int A[maxn]; // 原序列 queue<int> anss[maxm]; // 存储答案 int main(){ int n, m; cin >> n; len = (int)sqrt(n); // 块长度 for(int i = 1; i <= n; i++){ cin >> A[i]; } cin >> m; for(int i = 1; i <= m; i++){ int l, r; cin >> l >> r; queries[i] = Query(l, r, i); } sort(queries + 1, queries + m + 1); int L = 1, R = 1; buf[A[1]] = 1; for(int i = 1; i <= m; i++){ queue<int>& ans = anss[queries[i].ID]; Query &qi = queries[i]; while(R < qi.R) insert(A[++R]); while(L > qi.L) insert(A[--L]); while(R > qi.R) erase(A[R--]); while(L < qi.L) erase(A[L++]); for(map<int, int>::iterator it = buf.begin(); it != buf.end(); ++it){ if(it->second >= 2){ ans.push(it->first); } } } for(int i = 1; i <= m; i++){ queue<int>& ans = anss[i]; while(!ans.empty()){ cout << ans.front() << ' '; ans.pop(); } cout << endl; } }
在update的同时更新ans。(本题可考虑维护一个set,表示出现至少两次的数的集合)
诸神对凡人心生艳羡,厌倦天堂。