做前缀异或和,用堆维护一个五元组(x,l,r,p,v),x为区间右端点的值,l~r为区间左端点的范围,p为x在l~r中最大异或和的位置,v为该最大异或和,每次从堆中取出v最大的元素,以p为界将其切成两部分重新扔进堆即可。查询一个值在一个区间中的最大异或和用可持久化trie实现。luogu上T掉了。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<queue> using namespace std; #define ll long long #define N 500010 #define ui unsigned int char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} ui read() { ui x=0;char c=getchar(); while (c<'0'||c>'9') c=getchar(); while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x; } int n,k,goat,root[N],cnt; const ui I=1; ui a[N]; struct data{int ch[2],s,id; }trie[N*33]; void ins(int &k,ui x,int i,int ID) { trie[++cnt]=trie[k];k=cnt; trie[k].s++;trie[k].id=ID; if (i<0) return; ins(trie[k].ch[(x&(I<<i))>0],x,i-1,ID); } struct data2 { ui x;int l,r,p;ui d; bool operator <(const data2&a) const { return d<a.d; } }; priority_queue<data2> q; int query(int l,int r,ui x) { for (int i=31;~i;i--) { int op=(x&(I<<i))>0; if (trie[trie[r].ch[op^1]].s>trie[trie[l].ch[op^1]].s) l=trie[l].ch[op^1],r=trie[r].ch[op^1]; else l=trie[l].ch[op],r=trie[r].ch[op]; } return trie[r].id; } int main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif cin>>n>>k;ins(root[0],0,31,0); for (int i=1;i<=n;i++) { root[i]=root[i-1]; a[i]=a[i-1]^read(); ins(root[i],a[i],31,i); } root[-1]=0; for (int i=1;i<=n;i++) { int p=query(root[-1],root[i-1],a[i]); q.push((data2){a[i],0,i-1,p,a[i]^a[p]}); } ll ans=0; while (k--) { data2 t=q.top();q.pop();ans+=t.d;int x=t.p; if (x>t.l) { int p=query(root[t.l-1],root[x-1],t.x); q.push((data2){t.x,t.l,x-1,p,t.x^a[p]}); } if (x<t.r) { int p=query(root[x],root[t.r],t.x); q.push((data2){t.x,x+1,t.r,p,t.x^a[p]}); } } cout<<ans; return 0; }
场上写的是CF1055F的做法(当然没有隐式建trie了),代码还没拿到。