[十二省联考2019]异或粽子 题解
[十二省联考2019]异或粽子 题解
Problem
给定\(n\)个数\(A_i\),选择不同的\(m\)个区间\([L,R]\)使得\(\sum\limits_{i=1}^m \bigotimes\limits_{j=L_i}^{R_i}A_j\)最大
Solution
先做前缀和,使得\(A_i=\bigotimes\limits_{j=1}^i A_j\),这样题目转化成选出异或值最大的\(m\)对
至于怎么去算这个东西,可以尝试去二分一个答案下界,即这\(m\)个数都大于\(mid\),我们考虑怎么去判合不合法
枚举肯定不行,考虑一次算出以\(l\)为左端点时有多少个\(A_l \otimes A_r \geq mid(r > l)\),我们考虑把\([l+1,n]\)全部插入一个01Trie中,在Trie树上统计
记当前位\(A_l\)的值为\(k\),\(mid\)的值为\(p\)。倘若\(p=0\),则个数为\(k \otimes 1\)的节点个数加上递归进入\(k \otimes 0\)的答案;倘若\(p=1\),则个数为进入\(k \otimes 1\)的答案
统计答案时进入Trie中dfs即可
这样每次二分都要重建Trie树,持久化之后就不用每次都建了
这个算法说不定能过面对\(5 \times 10^5\)的数据还是略显乏力,考虑还能怎么优化
上面的算法的瓶颈在于二分,考虑直接在Trie上二分,这样可以少掉一个\(log\)
把所有数丢到Trie上一起二分,倘若当前结点右儿子的大小大于当前剩余对数,则全部往右儿子走,下界把当前位赋一;否则就往左儿子走
Code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
int n,m;
LL ans,lim,sum;
LL A[500005],cur[500005],root[500005];
struct Trie{
int tot;
int son[20000005][2],size[20000005];
void insert(int u,int p,int d,LL val){
size[u]=size[p]+1;
if(d==-1)
return;
int k=(val>>d)&1;
son[u][k]=++tot;
son[u][k^1]=son[p][k^1];
insert(son[u][k],son[p][k],d-1,val);
return;
}
LL solve(int d,int now,LL val){
if(d==-1)
return val;
LL sum=0;
for(register int i=1;i<=n;++i){
int k=(A[i-1]>>d)&1;
sum+=size[son[cur[i]][k^1]];
}
if(now>sum){
now-=sum;
for(register int i=1;i<=n;++i){
int k=(A[i-1]>>d)&1;
cur[i]=son[cur[i]][k];
}
return solve(d-1,now,val);
}
else{
for(register int i=1;i<=n;++i){
int k=(A[i-1]>>d)&1;
cur[i]=son[cur[i]][k^1];
}
return solve(d-1,now,val+(1LL<<d));
}
}
LL count(int u,int d,LL val,LL lim){
if(!u)
return 0;
if(d==-1)
return size[u];
int k=(val>>d)&1;
int p=(lim>>d)&1;
if(p)
return count(son[u][k^1],d-1,val,lim);
else
return size[son[u][k^1]]+count(son[u][k],d-1,val,lim);
}
LL Calcu(int u,int d,int op,LL val,LL lim,LL sum){
if(!u)
return 0;
if(d==-1)
return sum*size[u];
int k=(val>>d)&1;
int p=(lim>>d)&1;
if(op)
return Calcu(son[u][k^1],d-1,1,val,lim,sum+(1LL<<d))+Calcu(son[u][k],d-1,1,val,lim,sum);
else if(!p)
return Calcu(son[u][k^1],d-1,1,val,lim,sum+(1LL<<d))+Calcu(son[u][k],d-1,0,val,lim,sum);
else
return Calcu(son[u][k^1],d-1,0,val,lim,sum+(1LL<<d));
}
}T;
inline LL read(){
LL x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
int main(){
n=read();m=read();
for(register int i=1;i<=n;++i)
A[i]=A[i-1]^read();
T.tot=n;
for(register int i=1;i<=n;++i)
root[i]=i;
for(register int i=n;i>=1;--i)
T.insert(root[i],root[i+1],32,A[i]);
for(register int i=1;i<=n;++i)
cur[i]=root[i];
lim=T.solve(32,m,0);
for(register int i=1;i<=n;++i)
sum+=T.count(root[i],32,A[i-1],lim);
for(register int i=1;i<=n;++i)
ans+=T.Calcu(root[i],32,0,A[i-1],lim,0);
if(sum>m)
ans-=lim*(sum-m);//可能选多了,把多的减去
printf("%lld\n",ans);
return 0;
}