P4462 CQOI2018 異或序列

2022-02-17 15:05:41 字數 3941 閱讀 9585

原題鏈結  

給你乙個長度為 $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 開個桶,莫隊即可 注意一下莫隊處理時刪除乙個數時的順序就好了...