题目大意 :
询问一段区间内出现次数不为2的数字之和.
题解:
离线做法: 莫队. 因为数字在1e5范围内, 用一个cnt数组维护值为i的当前出现次数. 莫队移动更新的时候就维护cnt数组, 注意当cnt[a[i]] 为1, 2, 3的时候要特判, 因为出现次数为2的时候是没有贡献的.
在线做法: 用主席树差分, 主席树维护序列而不是维护值域. 建主席树的时候维护一个值上上次, 以及上上上次出现的位置, 将第i棵主席树的位置上将a[i]值插入, 再把a[i]上上次出现的位置(序列上的位置, 我们是维护序列的主席树)值修改为 -2 * a[i], 再把上上上次的位置的值修改为0. 查l - r的时候, 我们只需要查第r棵主席树中, 位置l-r的和. 因为我们对于某个位置上的值的上上次修改成 -2 * a[i], 所以如果l, r 包括了3个及以上相同的值的话, -2 * a[i]就会把对答案的贡献抵消掉. 举个例子, 33333这串数, 对于位置3上的主席树, 维护的序列长的是这个样子, -6, 3, 3, 0, 0(当前位置之后的是0), 对于位置5上的主席树维护的序列长的是, 0, 0, -6, 3, 3. 所以说不把上上上次修改为0的话, 那因为当前主席树先是继承之前的, 所以上上上次就会是-6, 那位置5 上的主席树就是: -6, -6, -6, 3, 3. 查询1, 5 在5的主席树里查, 1 - 5的变成了-12, 就非法了. 如果正常的: 0, 0, -6, 3, 3的话, 查询4 、5是6, 查询3 、5是0(3出现3次了). 注意对与l - r的询问我们是在r上的线段树查l-r的和.
主席树仔细想想也不难想, 当然莫队代码又短又无脑还能过... 不过强制在线或者n很大的话还是要用主席树...毕竟log的复杂度比根号的要高贵...
莫队代码:
#include<stdio.h>
#include<cmath>
#include<algorithm>
#define Mercer register int
using namespace std;
typedef long long dnt;
const int maxn = 200005;
dnt ed, ans[maxn];
int n, m, block, blo[maxn], a[maxn], cnt[maxn];
inline const int read(){
register int f = 1, x = 0;
register char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return f * x;
}
struct point{ int l, r, id;}q[maxn];
inline bool cmp(point x, point y){
return (blo[x.l] == blo[y.l]) ? (x.r < y.r) : (blo[x.l] < blo[y.l]);
}
inline void update(int x, int opt){
if(!opt){
--cnt[a[x]];
if(cnt[a[x]] > 2) ed -= a[x];
else if(cnt[a[x]] == 2) ed -= a[x] * 3;
else if(cnt[a[x]] == 1) ed += a[x];
else ed -= a[x];
return;
}
++cnt[a[x]];
if(cnt[a[x]] > 3) ed += a[x];
else if(cnt[a[x]] == 3) ed += a[x] * cnt[a[x]];
else if(cnt[a[x]] == 2) ed -= a[x];
else ed += a[x];
}
inline void Modui(){
for(int l = 1, r = 0, i = 1; i <= m; ++i){
while(l < q[i].l) update(l++, 0);
while(l > q[i].l) update(--l, 1);
while(r < q[i].r) update(++r, 1);
while(r > q[i].r) update(r--, 0);
ans[q[i].id] = ed;
}
}
int main(){
n = read(), m = read(), block = (int)sqrt(n);
for(Mercer i = 1; i <= n; ++i) a[i] = read();
for(Mercer i = 1; i <= m; ++i) q[i].l = read(), q[i].r = read(), q[i].id = i;
for(Mercer i = 1; i <= n; ++i) blo[i] = (i - 1) / block + 1;
sort(q + 1, q + m + 1, cmp);
Modui();
for(Mercer i = 1; i <= m; ++i) printf("%I64d\n", ans[i]);
return 0;
}