#trie#A 區間異或
阿新 • • 發佈:2020-11-02
題目
給定一個長度為\(n\)的序列,詢問有多少個\((l,r),1\leq l\leq r\leq n\)滿足
\[xor_{l\leq j\leq r}a_j\geq k \]分析
顯然跑一次字首和就變成查詢兩個數的異或值是否不少於\(k\)
那麼這顯然可以轉換成01trie的問題
按照二進位制位處理,分類討論:
- 當前\(x\)和\(k\)的二進位制位為1,那麼跳到\(trie[p][0]\)
- 當前\(x\)和\(k\)的二進位制位為0,那麼只要讓該二進位制位為1就能使異或值大於\(k\),然後累計答案再跳到\(trie[p][0]\)
- 當前\(x\)的二進位制位為0,\(k\)的二進位制位為1,那麼跳到\(trie[p][1]\)
- 當前\(x\)的二進位制位為1,\(k\)的二進位制位為0,那麼只要讓該二進位制位為0就能使異或值大於\(k\),然後累計答案再跳到\(trie[p][1]\)
程式碼
#include <cstdio> #include <cctype> #define rr register using namespace std; const int N=1000011; int a[N],n,m; long long ans; inline signed iut(){ rr int ans=0; rr char c=getchar(); while (!isdigit(c)) c=getchar(); while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar(); return ans; } struct Trie{ int trie[N*30][2],cnt[N*30],tot; inline void Clear(){ trie[1][0]=trie[1][1]=cnt[1]=0,tot=1; } inline void Insert(int x){ rr int p=1; for (rr int i=29;~i;--i){ rr int z=(x>>i)&1; if (!trie[p][z]){ trie[p][z]=++tot,cnt[tot]=0, trie[tot][0]=trie[tot][1]=0; } ++cnt[p],p=trie[p][z]; } ++cnt[p]; } inline signed query(int x){ rr int p=1,ans=0; for (rr int i=29;~i;--i){ if (!p) return ans; if ((x>>i)&1){ if ((m>>i)&1) p=trie[p][0]; else ans+=cnt[trie[p][0]],p=trie[p][1]; }else{ if ((m>>i)&1) p=trie[p][1]; else ans+=cnt[trie[p][1]],p=trie[p][0]; } } return ans+cnt[p]; } }trie; signed main(){ freopen("xor.in","r",stdin); freopen("xor.out","w",stdout); for (rr int T=iut();T;--T){ trie.Clear(),trie.Insert(0), n=iut(),m=iut(),ans=0; for (rr int i=1,x=0;i<=n;++i){ x^=iut(), ans+=trie.query(x), trie.Insert(x); } printf("%lld\n",ans); } return 0; }