題目鏈接:
http://codeforces.com/contest/617/problem/E
題目大意:
有n 個數,m個詢問。每次詢問在區間[l,r]裏面,有多少種情況使得ai^ai+1^……^aj=k。
範圍:1 ≤ n, m ≤ 100 000, 0 ≤ k ≤ 1 000 000。
思路:
對於這類區間上的問題,我們可以獲得數組的異或前綴和pre[i]=a1^a2......ai。
這樣對於區間[l,r],我們可以知道這個區間的異或值爲pre[l-1]^pre[r]。
對於不需要修改的區間詢問問題,可以採用莫隊算法。
莫隊算法:在我現在粗淺的認知看來,莫隊算法的主要精髓就是面對已知的詢問信息,將這些詢問進行順序調整然後離線操作。
例如對於多個詢問[l1,r1],[l2,r2]……[li,ri],如果已知了[l1,r1]的結果,那麼我們肯定很快能夠求出[l1+1,r1+1],[l1-1,r1+1],[l1-1,r1-1],[l1+1,r1-1]的結果。所以當我們去看第二個詢問的時候,我們就可以通過第一個詢問的結論,一步步地從[l1,r1]走向[l2,r2],此時我們所需要的時間就是O(|L2-L1|+|R2-R1|),這個有一個名字叫做曼哈頓距離。於是問題就變成了構造曼哈頓最小生成樹,然後按照生成樹的順序進行求解,使得速度大大提升。
但是求曼哈頓生成樹比較困難,然而還有一種分塊思想,能夠比較迅速而高效地解決這個問題。
我們把n個數分塊成爲sqrt(n)塊,然後把詢問進行排序:如果兩個詢問的l是在同一塊裏面的,就按r從小到大排。如果不同塊,就按照塊從小到大排。在同一個塊裏面轉移的時候,由於每個塊裏面有n^0.5個元素,所以轉移最大時間複雜度就是O((n^0.5)*(n^0.5))=O(n)。如果在不同塊,最大複雜度是O(n^1.5)。所以最終時間複雜度爲O(n^1.5)。
------------------------------------------------------------------------------
所以對於這個問題,我們已經知道了前綴和,所以可以知道:如果pre[i-1]^pre[j]=k,那麼就有pre[j]^k=pre[i-1]。這個時候我們開一個數組num,來記錄pre[i-1]^pre[j]=k時候的個數,我們轉移時就只需要對答案sum+=num[pre[i-1]^pre[j]]就可以了,再化簡一下就是,sum+=num[pre[j]^k]。
代碼:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<stdlib.h>
#define ll __int64
#define M 100010
using namespace std;
ll n,m,k,a[M],pre[M],ans[M],block,pos[M],num[M*50],sum;
struct node {
ll l,r,id;
}Q[M];
bool cmp(node a,node b )
{
if(pos[a.l]==pos[b.l])return a.r<b.r;
return pos[a.l]<pos[b.l];
}
int main()
{
ll i,j;
while(~(scanf("%I64d%I64d%I64d",&n,&m,&k)))
{
sum=0;
memset(num,0,sizeof(num));
memset(pre,0,sizeof(pre));
block=ceil(sqrt(1.0*n)); //分塊
for(i=1;i<=n;i++)
{
scanf("%I64d",&a[i]);
if(i==1)pre[i]=a[i];
else pre[i]=pre[i-1]^a[i]; //求前綴數組
pos[i]=(i-1)/block;
}
for(i=1;i<=m;i++)
{
scanf("%I64d%I64d",&Q[i].l,&Q[i].r);
Q[i].id=i;
// pos[i]=i/block;
}
sort(Q+1,Q+m+1,cmp);
ll L,R;
L=1;R=0;
num[0]++;
for(i=1;i<=m;i++)
{
ll id=Q[i].id;
while(R<Q[i].r){
R++;
sum+=num[pre[R]^k];
num[pre[R]]++; //記錄出現次數
}
while(R>Q[i].r)
{
num[pre[R]]--;
sum-=num[pre[R]^k];
R--;
}
while(L<Q[i].l)
{
num[pre[L-1]]--;
sum-=num[pre[L-1]^k];
L++;
}
while(L>Q[i].l)
{
L--;
sum+=num[pre[L-1]^k];
num[pre[L-1]]++;
}
// printf("%I64d %I64d\n",L,R);
ans[id]=sum;
}
for(i=1;i<=m;i++)
printf("%I64d\n",ans[i]);
}
}