题目分析:
一道近似的题目曾经出现在SCOI中,那题可以利用RMQ或者线段树做,这题如果用那种做法时间复杂度会是$log$三次方的。
采用一种类似于整体二分的方法可以解决这道题。
将序列的线段树模型建出来,将每个询问自顶向下找,要么被分到某个区间,要么在当前区间被分成两半。
对于某个区间$[l,r]$,可以找到一个$mid$,求出所有$[i,mid]$和$[mid+1,i]$的线性基。注意到这样的话每个数被插入线性基的次数是树高次,所以求出这些想要的线性基的复杂度是$O(nlog^2n)$。
对于每个被分成两半的区间,可以找到一个$[i,mid]$和$[mid+1,j]$,拼起来,拼起来的复杂度是$O(log^2n)$,每个询问只被拼起来一次,所以时间复杂度是$O((n+q)log^2n)$
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int maxn = 500500; 5 6 int n,a[maxn],q,cal[maxn],ans[maxn]; 7 pair<int,int> qy[maxn]; 8 struct bs{int p[21];}sl[maxn],rv[maxn]; 9 10 void read(){ 11 scanf("%d",&n); 12 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 13 scanf("%d",&q); 14 for(int i=1;i<=q;i++) scanf("%d%d",&qy[i].first,&qy[i].second); 15 } 16 17 int merge(bs alpha,bs beta){ 18 for(int i=0;i<20;i++){ 19 if(beta.p[i] == 0) continue; 20 for(int j=i;j>=0;j--){ 21 if(!(beta.p[i]&(1<<j))) continue; 22 if(alpha.p[j]) beta.p[i] ^= alpha.p[j]; 23 else {alpha.p[j] = beta.p[i];break;} 24 } 25 } 26 int as = 0; 27 for(int i=19;i>=0;i--)if((as^alpha.p[i]) > as) as ^= alpha.p[i]; 28 return as; 29 } 30 31 void solve(int tl,int tr,int l,int r){ 32 int mid = (tl+tr)/2; 33 for(int i=l;i<=r;i++) rv[i] = rv[0]; 34 for(int i=mid;i>=tl;i--){ 35 sl[i] = sl[i+1]; int hh = a[i]; 36 for(int j=19;j>=0;j--){ 37 if(!((1<<j)&hh)) continue; 38 if(sl[i].p[j]) hh ^= sl[i].p[j]; 39 else{sl[i].p[j] = hh; break;} 40 } 41 } 42 for(int i=l;i<=r;i++) rv[i] = sl[qy[i].first]; 43 for(int i=tl;i<=mid;i++) sl[i] = sl[0]; 44 for(int i=mid+1;i<=tr;i++){ 45 sl[i] = sl[i-1]; int hh = a[i]; 46 for(int j=19;j>=0;j--){ 47 if(!((1<<j)&hh)) continue; 48 if(sl[i].p[j]) hh^=sl[i].p[j]; 49 else {sl[i].p[j] = hh; break;} 50 } 51 } 52 for(int i=l;i<=r;i++) ans[cal[i]] = merge(rv[i],sl[qy[i].second]); 53 for(int i=mid+1;i<=tr;i++) sl[i] = sl[0]; 54 } 55 56 void divide(int tl,int tr,int l,int r){ 57 if(l > r) return; 58 if(tl == tr){for(int i=l;i<=r;i++) ans[cal[i]] = a[tl]; return;} 59 int mid = (tl+tr)/2,num = l-1; 60 for(int i=l;i<=r;i++) 61 if(qy[i].second<=mid)num++,swap(cal[i],cal[num]),swap(qy[i],qy[num]); 62 divide(tl,mid,l,num); 63 int num2 = num; 64 for(int i=num+1;i<=r;i++) 65 if(qy[i].first>mid)num2++,swap(cal[i],cal[num2]),swap(qy[i],qy[num2]); 66 divide(mid+1,tr,num+1,num2); 67 solve(tl,tr,num2+1,r); 68 } 69 70 int main(){ 71 read(); 72 for(int i=1;i<=q;i++) cal[i] = i; 73 divide(1,n,1,q); 74 for(int i=1;i<=q;i++) printf("%d ",ans[i]); 75 return 0; 76 }