【線段樹與01trie樹】woj 2645 hyc的xor/mex
阿新 • • 發佈:2018-11-10
描述
NOIP2017就要來了,備戰太累,不如做做hyc的新題?
找回自信吧!
一句話題意:n個數,m個操作
操作具體來講分兩步
1.讀入x,把n個數全部xor上x
2.詢問當前n個數的mex
意味著每次操作後你都需要輸出一次
(注意:是mex,即集合內未出現過的最小非負整數
舉2個例子 mex(4,33,0,1,1,5)=2 mex(1,2,3)=0)
輸入
第一行兩個整數n,m 意義同題面(1 ≤ n, m ≤ 3 * 10^5)
第二行 n個數 ai (0 ≤ ai ≤ 3 * 10^5)
接下來 m 行
每行一個整數 x
表示將所有數xor上x (0 ≤ x ≤ 3 * 10^5).
輸出
一共m行
每行表示當前n個數的xor
樣例輸入[複製]
5 4
0 1 5 6 7
1
1
4
5
樣例輸出[複製]
2
2
0
2
提示
30%資料n,m<=1000
100%資料同“輸入”
標籤
mogician原創
異或操作經常都跟trie樹有關。異或具有結合律,那麼可以把每次異或的值用一個數組記錄下來,最後再跟原序列去異或。
首先建樹,把每個結點的值域區間記錄一下。
然後原序列用一個權值線段樹維護一下。
然後把原來的線段樹看做是trie樹,左兒子就是0,右兒子就是1.
然後是query操作。大概是二進位制位的貪心。先一直往左走。然後如果某一位需要異或1,就往反方向走,因為相當於是把左右子樹交換了一下。如果需要異或0,就不變。如果一個結點所代表的值域中的數都出現過。。【tr[tmp].cnt==tr[tmp].r-tr[tmp].l+1】就給答案加上2^depth,然後調頭走另一條路。
具體看程式碼吧。%ldx
#include<bits/stdc++.h> using namespace std; const int inf=(1<<19)-1; inline void read(int &x){ x=0;char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); } struct trnode{ int l,r,cnt; }tr[1211111]; int n,m,x; int s[20]; inline void pushup(int root){ tr[root].cnt=tr[root<<1].cnt+tr[root<<1|1].cnt;} inline void build(int root,int l,int r){ tr[root].l=l,tr[root].r=r; if(l==r){ tr[root].cnt=0; return; } int mid=(tr[root].l+tr[root].r)>>1; build(root<<1,l,mid); build(root<<1|1,mid+1,r); } void update(int root,int x){ if(tr[root].l==tr[root].r){ tr[root].cnt=1; return; } int mid=tr[root].l+tr[root].r >>1; if(x<=mid) update(root<<1,x); else update(root<<1|1,x); pushup(root); } int query(int root,int depth){ if(tr[root].l==tr[root].r) return 0; int tmp=(root<<1)|s[depth]; if(tr[tmp].cnt==tr[tmp].r-tr[tmp].l+1) return query(tmp^1,depth-1)+(1<<depth); return query(tmp,depth-1); } int main(){ read(n),read(m); build(1,0,inf); while(n--){ read(x); update(1,x); } while(m--){ read(x); for(int i=1;i<=19;++i) if(x&(1<<i)) s[i]^=1; printf("%d\n",query(1,18)); } }