51Nod1577 異或湊數 線性基
阿新 • • 發佈:2018-10-31
原文連結https://www.cnblogs.com/zhouzhendong/p/51Nod1577.html
題意
給定一個長度為 n 的序列。
有 m 組詢問,每一組詢問給出 L,R,k ,詢問 L,R 區間內是否能找出一些數,使它們 XOR 起來等於 k 。
$n,m\leq 5\times 10^5, 0\leq a_i,k< 2^{30}$
題解
由於 $n,m$ 同階,所以以下時間複雜度描述時,對於 $n,m$ 不加區分。
線性基合併是 $O(\log ^2 a_i)$ 的。
直接線段樹維護區間線性基或者 ST 表複雜度均為 $O(n\log ^3 a_i)$ 。
CDQ分治時間複雜度為 $O(n\log ^2 a_i)$ 。
以上演算法均不能通過。
考慮將詢問離線,按照 R 從小到大排序。
我們將 $a_i$ 從左到右依次加入。利用線性基維護儘量靠右的基向量即可(經典套路)。
時間複雜度為 $O(n\log a_i)$ 。
程式碼
#include <bits/stdc++.h> using namespace std; int read(){ int x=0; char ch=getchar(); while (!isdigit(ch)) ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } const int N=500005; int n,m; int a[N]; struct xxj{ int v[30],p[30]; void clear(){ memset(v,0,sizeof v); memset(p,0,sizeof p); } void insert(int x,int y){ for (int i=29;i>=0;i--) if (~x>>i&1) continue; else if (!v[i]){ v[i]=x,p[i]=y; break; } else { if (y>p[i]) swap(y,p[i]),swap(x,v[i]); x^=v[i]; } } int query(int x,int y){ for (int i=29;i>=0;i--) if (x>>i&1) if (!v[i]||p[i]<y) return 0; else x^=v[i]; return 1; } }xianxingji; struct Query{ int L,R,k,id,ans; }q[N]; bool cmpR(Query a,Query b){ return a.R<b.R; } bool cmpid(Query a,Query b){ return a.id<b.id; } int main(){ n=read(); for (int i=1;i<=n;i++) a[i]=read(); m=read(); for (int i=1;i<=m;i++){ q[i].L=read(); q[i].R=read(); q[i].k=read(); q[i].id=i; } sort(q+1,q+m+1,cmpR); xianxingji.clear(); for (int i=1,j=0;i<=m;i++){ while (j<q[i].R) j++,xianxingji.insert(a[j],j); q[i].ans=xianxingji.query(q[i].k,q[i].L); } sort(q+1,q+m+1,cmpid); for (int i=1;i<=m;i++) puts(q[i].ans?"YES":"NO"); return 0; }