原題鏈結
給你乙個長度為 $n$ 的序列和 $m$ 次詢問,求每次詢問中有多少個子區間異或和為 $k$;
這是一道區間查詢的題目,所用的演算法是資料結構,我這裡用的是莫隊演算法;
回顧一下異或的神奇性質:
$1.a$ ^ $b = c$ ,則 $a$ ^ $ c = b$ ,$b$ ^ $c = a$ ;
$2. a$ ^ $a = 0$ ,$a$ ^ $0 = a$ ,則 $a$ ^ $a$ ^ $a = a$ ;
有了上面的性質,我們就可以把這一長串的異或化簡一下了:
但是有些同學會問了:這不是更麻煩了嘛?怎麼叫化簡了呢?
我們可以用 $s_i$ 來表示區間 $[ 1,i ]$ 的異或和,即 $s_i = a_1$ ^ $a_2$ ^ $a_3$ …… ^ $a_i$ ;
這樣我們就將一串連續的數異或轉化為了兩個數的異或!
所以我們在求有多少個子區間時就是求有多少個 $s_j$^ $s_= k ( l <= i <= j <= r )$ ;
接下來我們考慮區間轉移對當前答案造成的影響:
假如當前區間向右擴充套件了乙個 $a [ i+4]$ ,那麼它對答案造成的影響取決於以 $a [ i+4 ]$ 為結尾的若干區間中有多少個區間異或和為 $k$;
我們列舉出所有以 $a [ i+4 ]$ 為結尾的區間:
$1. a [ i ]$ ^ $a [ i+1 ]$ ^ $a [ i+2 ]$ ^ $a [ i+3 ]$ ^ $ a [ i+4 ] = s_$ ^ $s_ = k ?$
$2. a [ i+1 ]$ ^ $a [ i+2 ]$ ^ $a [ i+3 ]$ ^ $a [ i+4 ] = s_$ ^ $s_i = k ?$
$3. a [ i+2 ]$ ^ $a [ i+3 ]$ ^ $a [ i+4 ] = s_$ ^ $s_ = k ?$
$4. a [ i+3 ]$ ^ $a [ i+4 ] = s_$ ^ $s_ = k ?$
$5. a [ i+4 ] = s_$^ $s_ = k ?$
但是我們這樣暴力搞的話時間複雜度不允許啊,莫隊的每步轉移都要控制在 $o (1)$ 的時間複雜度內;
想一想,如果有 $s_$^ $s_x = k$ ,那麼我們只需知道區間內有多少個異或字首和為 $s_x$ ,答案就增加多少;
所以我們開乙個陣列 $times [ i ]$ 表示當前區間內有多少個異或字首和為 $i$ ;
那麼我們根據性質一可以得出:$s_x = s_$ ^ $k$ ,所以答案會增加 $times [ s_$^ $k ]$ ;
這樣我們就 $o(1)$ 地解決了問題;
再看個例子:
假如當前區間的左端點向右轉移了乙個單位,那麼我們要刪除 $a [ i ]$ 對答案的影響,它對答案造成的影響取決於以 $a [ i ]$ 為開頭的若干區間中有多少個區間異或和為 $k$;
我們列舉出所有以 $a [ i ]$ 為開頭的區間:
$1. a [ i ]$ ^ $a [ i+1 ]$ ^ $a [ i+2 ]$ ^ $a [ i+3 ]$ ^ $a [ i+4 ] = s_$ ^ $s_ = k ?$
$2. a [ i ]$ ^ $a [ i+1 ]$ ^ $a [ i+2 ]$ ^ $a [ i+3 ] = s_$ ^ $s_ = k ?$
$3. a [ i ]$ ^ $a [ i+1 ]$ ^ $a [ i+2 ] = s_$ ^ $s_ = k ?$
$4. a [ i ]$ ^ $a [ i+1 ] = s_$ ^ $s_ = k ?$
$5. a [ i ] = s_$ ^ $s_ = k ?$
所以就是求有多少個$s_x$ ^ $s_= k$(注意這裡是$s_$而不是$s_i$),那麼答案減少 $times [ s_$ ^ $k ]$ ;
這裡就是提醒大家在處理左端點時一定要注意 $-1$ 的問題,非常嚴重!(我就是這裡卡了 $2h$
核心內容講完了,剩下的就是一些莫隊演算法的內容了,建議不會莫隊的童鞋先學會莫隊再食用本**哦~
$code$:
#include#include#include
#include
using
namespace
std;
long
long
read()
while(ch>='
0'&&ch<='9'
)
return a*x;
}const
long
long n=1e5+5
;long
long n,m,k,nl=1
,nr,len,num,ans;
long
long
a[n],l[n],r[n],s[n],ans[n],pos[n],times[n];
//times[i]:當前區間中異或和為i的字首和有幾個
struct
node
a[n<<2
];bool cmp(node x,node y) //
奇偶性排序
void add(long
long x) //
統計新增點a[x]的貢獻
void del(long
long x) //
刪除點a[x]的貢獻
intmain()
len=num=sqrt(n); //
每塊的長度及要分成多少塊
for(long
long i=1;i<=num;i++)
if(r[num]//
如果這些塊不能包含所有的數,那麼就再開乙個塊將剩下的數全放進去
for(long
long i=1;i<=m;i++) //
將m次詢問的左右端點存下,待會離線處理
sort(a+1,a+1+m,cmp); //
將所有的塊進行奇偶性排序
times[0]=1; //
一開始當前區間沒有任何元素
for(long
long i=1;i<=m;i++)
for(long
long i=1;i<=m;i++) printf("
%lld\n
",ans[i]); //
按照詢問順序輸出答案
return0;
}
P4462 CQOI2018 異或序列
求出異或字首和sum,對於 a l a l 1 a r 就變成了sum r sum l 1 所以最終我們要求的就是在區間 l,r 中有多少子區間 l,r 是滿足 sum r sum l 1 k 的 sum r sum l 1 k sum l 1 sum r k sum r k sum l 1 變換以...
P4462 CQOI2018 異或序列 莫隊
區間異或 sum l,r pr e l 1 xo rpre r sum l,r pre l 1 xor pre r sum l,r p re l 1 x orpr e r a xo rb k axo rk b a xor b k implies a xor k b axorb k ax ork b ...
LuoguP4462 CQOI2018 異或序列
鏈結 異或的逆運算就是其本身 即a xor b c 即 c xor a b 對於本題,記錄一下1 i的異或和為sum i 顯然l r的異或和就為sum l 1 xor sum r 若為異或和為k即滿足sum r xor k sum l 1 開個桶,莫隊即可 注意一下莫隊處理時刪除乙個數時的順序就好了...