邀请赛之前可能只会更这一次了吧QAQ
久闻莫队算法的大名,号称是“可以解决任何区间问题”的算法,今天就来稍微说一下莫队算法。
这个算法是一位名叫莫涛的国家队队长发明的算法,所以尊称为莫队算法
莫队的原版文章里面的题目有一定难度,所以可以先看一下这道例题:
Description
有n个数字,给出k,以及m个查询。
每次查询的格式是L,r,求L~r(左右包含)这个区间内数字的出现次数刚好是k的数字种数。
范围:n<=30000,k<=n,m<=30000,1<=L
输入n,k,m,然后n个元素,然后m行查询,对于每一个询问,输出正确的答案。
Example input:
5 2 3
1 2 3 2 2
1 2
2 4
1 5
Example output:
0
1
0
关于这道题,最直观的感觉就是暴力大法好。对于每一次询问,我们都遍历L~r的所有数字,并记录出现次数为k的数字。这样的话复杂度是O(n*m)的,肯定是过不了的。
再来看一下MyZhY提出的一种方法。
设置两个指针left和right,分别指向被查询的区间左右端点L,r,然后将这两个指针不断地移动到下一个待查区间,每次移动的时候把移动的位置上的数字出现次数+1。这么做当然是可以的,但最坏情况下,每次询问移动的大小仍有可能是n次,复杂度依旧没有改变,甚至可能更慢。
但是莫队算法就是基于这种思想实现的。莫队首先给这个序列分块。什么是分块?顾名思义就是把一个完整的序列分割成若干个大小近似相同的块,维护这些块的性质就可以了。分块的时候一般是分成√n(向上取整)个块(除了最后一个块可能不完整),这样每个块里的元素近似等于√n了。我们给这些块按左端点L排序,每个块内部按右端点排序,这样一来就会有神奇的事情发生。我们仍然考虑以上的双指针思路,并分成几种情况:
1、待查询的区间完全包含在一个块内。这样直接遍历块内的所有元素就可以了,由于最多√n个元素,所以left指针最多移动√n次,而right指针则未知,最多移动n次,故复杂度是O(n√n)
2、待查询的区间跨越了不同块。则right最多也是移动√次,由于有n个块,所以最终的复杂度也是O(n√n),而left在每一次询问下也最多移动√n次,共m√n次。
由此,该算法的总复杂度为O((m+n)√n),足以应付大部分的情况了。
总结一下,莫队算法的用途是处理一系列离线的,一般是不带修改的区间查询问题。带修改的莫队的话则需要把二元组[left,right]变成三元组[left,right,x],代表询问left到right这个区间在经过x次修改后的答案。另外还有一些什么树上莫队,草丛莫队(提莫?)之类的骚操作我也不会QAQ
今天的例题CodeForces 617E如下:
Bob has a favorite number k and ai of length n. Now he asks you to answer m queries. Each query is given by a pair li and ri and asks you to count the number of pairs of integers i and j, such that l ≤ i ≤ j ≤ r and the xor of the numbers ai, ai + 1, ..., aj is equal to k.
The first line of the input contains integers n, m and k (1 ≤ n, m ≤ 100 000, 0 ≤ k ≤ 1 000 000) — the length of the array, the number of queries and Bob's favorite number respectively.
The second line contains n integers ai (0 ≤ ai ≤ 1 000 000) — Bob's array.
Then m lines follow. The i-th line contains integers li and ri (1 ≤ li ≤ ri ≤ n) — the parameters of the i-th query.
Print m lines, answer the queries in the order they appear in the input.
6 2 3 1 2 1 1 0 3 1 6 3 5
7 0
5 3 1 1 1 1 1 1 1 5 2 4 1 3
9 4 4
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.
题意:给你一串数字,每次问你某个区间的所有子区间中异或和为k的有多少。
分析:可以看作莫队的模板题,我们先求区间的异或前缀和xor[],然后对前缀和做莫队算法。根据异或的性质,xor[l-1]^xor[r]==k等价于k^xor[r]=xor[l-1]或者xor[l-1]^k=xor[r].某区间[i,j]的异或和就是xor[i-1]^xor[j].
代码如下:
#include
#include
#include
#include
#include
#include
#include
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 2e6+7;
struct node {int l,r,id;}q[maxn];
int a[maxn],pos[maxn],flag[maxn];
LL ans[maxn],sum[maxn];
int n,m,k;
LL Ans=0;
bool cmp(node a,node b)
{
if(pos[a.l]!=pos[b.l]) return pos[a.l]l){dele(l-1);l++;}
while(q[i].rr){r++;add(r);}
ans[q[i].id]=Ans;
}
for(i=1;i<=m;i++) printf("%I64d\n",ans[i]);
}
return 0;
}