Codeforces1100F Ivan and Burgers 【整体二分】【线性基】

题目分析:

一道近似的题目曾经出现在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\n",ans[i]);
75     return 0;
76 }

 

posted @ 2019-01-18 10:22  menhera  阅读(305)  评论(0编辑  收藏  举报