1. 程式人生 > 其它 >奶牛異或(字典樹求異或極值)

奶牛異或(字典樹求異或極值)

技術標籤:題解

原題連結
題意:給你n個數求一段區間[l,r]使得該區間的異或和最大,若有多個,求區間右端點最小的那個,如果還有多個,求區間長度最段的那個

思路:先求一個異或字首和,假設最後答案是[x,y]區間,那麼就是[1,x]區間的異或和 與 [1,y]區間的異或和 異或的結果,用為每一個字首和構造0-1字典樹(從高位到低位),然後列舉字首和pre[i] 在字典樹上查詢。

同樣的,也可以求樹上的一條路徑(u,v)間的異或極值
在這裡插入圖片描述
程式碼:

//#pragma GCC optimize("Ofast")
//#pragma GCC target("avx,avx2,fma")
//#pragma GCC optimization ("unroll-loops") #include <bits/stdc++.h> #define pb push_back #define ll long long #define IOS std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; int n,a[100005]; int pre[100005],val; int p[100]; map<int,int> Map; int tree[3500000][3],k=1; void
insertt() { int os=0; for(int i=0;i<22;i++) { int c=p[i]; if(tree[os][c]==0) { tree[os][c]=k; k++; } os=tree[os][c]; } } void query(ll x) { int os=0; for(int i=21;i>=0;i--) { int c=(x>>i)&
1; if(c==0) { if(tree[os][1]!=0) {val+=(1<<i); os=tree[os][1];} else {os=tree[os][0];} } else { if(tree[os][0]!=0) {os=tree[os][0];} else {val+=(1<<i); os=tree[os][1];} } } } int main() { cin>>n; for(int i=1;i<=n;i++) {cin>>a[i];} if(n==1) { cout<<a[1]<<" 1 1"<<endl; return 0; } for(int i=1;i<=n;i++) { pre[i]=pre[i-1]^a[i]; if(Map[pre[i]]==0) {Map[pre[i]]=i;} int op=pre[i],cnt=0; for(int j=21;j>=0;j--) { p[cnt++]=(op>>j)&1; } insertt(); } int maxx=0,px=0,py=0,maxp,minp; for(int i=1;i<=n;i++) { val=0; query(pre[i]); if(val==0){maxp=i;minp=i;} else{maxp=max(Map[val],i);minp=min(Map[val],i);} if((val^pre[i])>maxx) { maxx=val^pre[i]; px=minp; py=maxp; } if((val^pre[i])==maxx) { if(maxp<py) { px=minp; py=maxp; } if(maxp==py) { if(minp>px) { px=minp; py=maxp; } } } } if(px==py) {cout<<maxx<<" "<<px<<" "<<py<<endl;} else {cout<<maxx<<" "<<px+1<<" "<<py<<endl;} return 0; }