莫队算法模板
莫队算法除了add 和 del 其他部分基本不变,就拿一题的题解来当模板用吧。
http://codeforces.com/contest/617/problem/E
题意:
给你
n
n
个数,个查询和一个数值
k
k
,然后每次查询输入并问你区间[l,r]内有几对
i,j
i
,
j
使得
ai⨁ai+1⨁...⨁aj=k
a
i
⨁
a
i
+
1
⨁
.
.
.
⨁
a
j
=
k
。(
⨁
⨁
是异或的意思)
数据范围打开链接看就行
题解:
对于这题我们可以通过处理前缀和算出
ai⨁ai+1⨁...⨁aj=k
a
i
⨁
a
i
+
1
⨁
.
.
.
⨁
a
j
=
k
的值,实际上
ai⨁ai+1⨁...⨁aj=sum[i−1]⨁sum[j]
a
i
⨁
a
i
+
1
⨁
.
.
.
⨁
a
j
=
s
u
m
[
i
−
1
]
⨁
s
u
m
[
j
]
。
知道这个公式之后,我们再讲讲怎么用莫队处理
我们用一个数组flag[]储存在查询过程中前缀和出现了多少次。为什么要这样做呢?这是因为
sum[i−1]⨁sum[i−1]⨁sum[j]=sum[j]=sum[i−1]⨁k
s
u
m
[
i
−
1
]
⨁
s
u
m
[
i
−
1
]
⨁
s
u
m
[
j
]
=
s
u
m
[
j
]
=
s
u
m
[
i
−
1
]
⨁
k
每当我们在莫队的原始区间进行新查询的时候都会增加或减少一个前缀和,因此当出现区间变动的时候,我们就需要找能去当前缀和异或和为
k
k
<script type="math/tex" id="MathJax-Element-11">k</script>的前缀和的个数,然后对Ans进行加减。
值得注意的是这题异或出来的数可能比1e6还大所以flag要开大点。
Examples
input
6 2 3
1 2 1 1 0 3
1 6
3 5
output
7
0
input
5 3 1
1 1 1 1 1
1 5
2 4
1 3
output
9
4
4
Note
In the first sample the suitable pairs of i and j for the first query are: (1, 2), (1, 4), (1, 5), (2, 3), (3, 6), (5, 6), (6, 6). Not a single of these pairs is suitable for the second query.
In the second sample xor equals 1 for all subarrays of an odd length.
#include<bits/stdc++.h>
using namespace std;
const int maxn=1<<20;
int a[maxn];
int pos[maxn];
long long flag[maxn];
long long ans[maxn];
long long Ans=0;
struct node
{
int l,r,id;
}q[maxn];
bool cmp(const node &a,const node &b)
{
if(pos[a.l]==pos[b.l])
return a.r<b.r;
return pos[a.l]<pos[b.l];
}
int k;
void add(int x)
{
Ans+=flag[a[x]^k];
flag[a[x]]++;
}
void del(int x)
{
flag[a[x]]--;
Ans-=flag[a[x]^k];
}
int main()
{
int T,n,i,j,m,temp;
scanf("%d%d%d",&n,&m,&k);
int sz=sqrt(n);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i]=a[i-1]^a[i];
pos[i]=i/sz;
}
flag[0]=1;
for(i=1;i<=m;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
sort(q+1,q+m+1,cmp);
int L=1,R=0;
for(i=1;i<=m;i++)
{
while(L<q[i].l)
{
del(L-1);
L++;
}
while(L>q[i].l)
{
L--;
add(L-1);
}
while(R<q[i].r)
{
R++;
add(R);
}
while(R>q[i].r)
{
del(R);
R--;
}
ans[q[i].id]=Ans;
}
for(i=1;i<=m;i++)
printf("%I64d\n",ans[i]);
}