【2019 Multi-University 1 B】Operation 题解
题目大意
有一个长度为 n 的序列 a1⋯an,有 m 个操作,操作有两种:
0 l r:选择 al⋯ar 的一个子序列,使得其异或和最大,求该异或和;
1 x:a[++n]=x;
强制在线。
数据组数 T≤10,时限 4s
n,m≤5×105, (∑n),(∑m)≤106, 0≤x,ai≤230
题解
吹爆优秀的队友想出了优秀的 O(nlogai) 做法!
子序列最大异或和肯定是线性基了,直接线段树维护区间线性基有 3 个 log 就会 T 掉。
于是考虑每个点 i 维护 1~i 的线性基。对于点 i,首先它要继承 i−1 的线性基,然后把 ai 加进去,但不是普通的加,而是更新式地加。假设 ai 的最高位是 w,若线性基里 w 这个位为空,则直接加进去;若这个位放了一个数 yw,并且它比较旧(在原序列中的下标比 i 小),则把 yw 替换成 ai,然后 yw 当作一个新来的数继续插入线性基。
这样一来,相当于我所用的基元都是最新的,最靠近 i 的。
那么询问 [l,r] 的时候,就看 r 的线性基,先把 l 之前的基元去掉,然后剩下的基元求最大异或和就行了。
这样的时间就是 O(nlogai) 的了。
代码
//来自队友
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #include<stdio.h> #include<algorithm> #include<vector> #include<cstring> #define cint const int & using namespace std; typedef long long ll; #define rep(i,l,r) for (int i=l;i<=r;i++) int n,m,tt,a[1005000],pos[1005000][32],val[1005000][32]; void insert(int x,int xval){ rep(i,0,30) { pos[x][i]=pos[x-1][i]; val[x][i]=val[x-1][i]; } int tmp=x; for (int i=30;i>=0;i--) if (xval&(1<<i)){ if (!val[x][i]) { pos[x][i]=tmp; val[x][i]=xval; break; } else { if (pos[x][i]<tmp){ swap(pos[x][i],tmp); swap(val[x][i],xval); } xval^=val[x][i]; } } } int main() {
scanf("%d",&tt); while (tt--){ scanf("%d%d",&n,&m); rep(i,1,n) { scanf("%d",&a[i]); insert(i,a[i]); } int ans=0; rep(i,1,m){ int op; scanf("%d",&op); if (op==1){ int x; scanf("%d",&x); x^=ans; ++n; insert(n,x); } else { int l,r; scanf("%d%d",&l,&r); l=(l^ans)%n+1; r=(r^ans)%n+1; if (l>r) swap(l,r); ans=0; for (int j=30;j>=0;j--){ if (pos[r][j]>=l&&val[r][j]) ans=max(ans,ans^val[r][j]); } printf("%d\n",ans); } } rep(i,0,n) rep(j,0,30) val[i][j]=pos[i][j]=0; } }
|