1. 程式人生 > >莫隊演算法入門 Codeforces617E

莫隊演算法入門 Codeforces617E

莫隊演算法:

莫隊演算法的用處是,對於一個區間內的查詢,當我們已經知道了 [ L , R ] 的答案的時候,有莫隊演算法可以在很短的時間內得到 [ L - 1 , R ] 或者是 [ L ,R + 1 ]的答案,前提是可以離線處理。 步驟: 1.輸入 N 個點的時候,對每個點的進行分塊 ,一般 N 個點分成 sqrt(n) 塊 ,用一個pos[]陣列記錄分塊
int s = sqrt(n);
for(int i = 1;i <= n;i++){
	scanf("%d",&a[i]);
	a[i] ^= a[i - 1];	
	pos[i] = i / s;		//記錄所在分塊 
}
2.輸入m個詢問,將它儲存下來之後,對它進行排序,按照詢問的左端點所在塊排序,同塊則按右端點排序,都是從小到大
bool cmp(node a,node b){
	if(pos[a.l] != pos[b.l])	//兩個詢問的左極限如果在不同塊,那麼按分塊順序從小到大,否則按右極限 
		return pos[a.l] < pos[b.l];
	return a.r < b.r;
}
3.開始處理詢問,從前往後,一次挪動處理,由於有了前面的排序,後面的移到次數被大大減少了,所以時間很短 然後一邊記錄答案,最後輸出就好。 最簡單的例題 : Codeforces 617E E. XOR and Favorite Number time limit per test 4 seconds memory limit per test 256 megabytes input standard input output standard output

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.

Input

The first line of the input contains integers n

m and k (1 ≤ n, m ≤ 100 0000 ≤ 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.

Output

Print m lines, answer the queries in the order they appear in the input.

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: (12), (14), (15), (23), (36), (56), (66). 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.

題意:給出一個數組,下標從 1 到 n ,有 m 次詢問,一個值 k 每次詢問給出一個區間範圍,問區間範圍內連續異或值為 k 的子區間有多少個 思路:區間問題,並且可以離線處理,所以莫隊是最好選擇。 首先要考慮異或的特性  x ^ k = 0 表示x = k ,所以我們可以把區間裡的異或值拿去 異或 k,結果為 0 就符和條件 還有就是 如果前面出現異或結果為 y ,而你當前的x ^ k = y 的話,那麼表示 x ^ y = k,也滿足條件。 還有就是題目要求的是連續的子區間,所以我們得做字首和才方便處理 . 字首陣列 a[] 開始處理詢問,對於每個詢問,用 add() 函式 和 dele() 函式去移動到對應的位置
void add(int x){
	Ans += flag[a[x] ^ k];	
	flag[a[x]]++;
}
Ans記錄符合子區間個數,flag[x] 存的是已有異或值為x 的區間的個數 ,flag[0] = 1 ,因為x ^ k = 0 表示 x = k 然後 flag 記數 異或值區間 ,當你下一次的異或值 X去 異或 K = Y,而flag[Y]又有值的時候,表示有 flag[Y] 個區間去異或 X 可以等於 K ,表示 [1,a] ^ [1,b] = k ,也就是說 [b,a] = k,又有 flag[Y] 個區間是滿足條件,所以計數可以增加flag[y]個。
void dele(int x){
	flag[a[x]]--;
	Ans -= flag[a[x] ^ k];
}
dele陣列就比較簡單了,減去它的貢獻就好了。 a[x] ^ k = z ,所以 a[x] ^ z = k,減去了 a[x],那麼原本 有 flag[z]個區間是滿足的,得把它減掉 注意補充:在對左指標進行操作的時候,比如我左指標要右移一位,那麼就是要刪除當前所在位置的數的貢獻值 但是,我們並不能 dele(l++) 而是 dele(l - 1) , l++; 原因是,我們是做了一個 異或字首和 a[ l ] = x1 ^ x2 ^ ... ^xl ,那麼 如果要單找 xl 的話,不是直接 a[ l ] 可以得到的 a[ l ] = a[ l ] ^ a[ l - 1 ] ,也就是說 xl 的貢獻是由 a[ l - 1 ] 得來的,所以當我們要刪去 x l 的貢獻的時候,我們需要dele 的是 a[ l - 1 ] 。 同理,左移的時候要先 l -- 移動到 位置,然後 add(l - 1) 才能把 l 的貢獻增加上去 AC程式碼:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define maxn 2000006
#define mem(a,x) memset(a,x,sizeof(a))
#define ll long long

struct node{ //記錄詢問,離線處理 
	int l,r,id;
}q[maxn];

int a[maxn],pos[maxn];	// a是字首陣列,pos用於分塊 
ll ans[maxn],sum[maxn],flag[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] < pos[b.l];
	return a.r < b.r;
}
void add(int x){
	Ans += flag[a[x] ^ k];	
	flag[a[x]]++;
}
void dele(int x){
	flag[a[x]]--;
	Ans -= flag[a[x] ^ k];
}
int main(){
	int i;
	while(scanf("%d %d %d",&n,&m,&k) != EOF){
		Ans = 0;
		mem(flag,0);
		int s = sqrt(n); //有n個點,分成 sqrt(n)個塊 
		flag[0] = 1; // x 異或 k 之後等於0表示 x 等於 k,記錄一個貢獻 
		for(int i = 1;i <= n;i++){
			scanf("%d",&a[i]);
			a[i] ^= a[i - 1];	//記錄異或字首和 
			pos[i] = i / s;		//記錄所在分塊 
		}
		for(int i = 1;i <= m;i++){
			scanf("%d %d",&q[i].l,&q[i].r);
			q[i].id = i;
		}
		sort(q + 1,q + 1 + m,cmp);
		int l = 1,r = 0;
		for(int i = 1;i <= m;i++){ //對於每個查詢 
			while(q[i].l < l){ //把左右極限移到對應位置 
				l--;
				add(l - 1);
			}
			while(q[i].l > l){
				dele(l - 1);
				l++;
			}
			while(q[i].r < r){
				dele(r);
				r--;
			}
			while(q[i].r > r){
				r++;
				add(r);
			}
			ans[q[i].id] = Ans;
		}
		for(int i = 1;i <= m;i++)
			printf("%lld\n",ans[i]);
	}
	return 0;
}