题解-luogu-p5283异或粽子

题目链接

给定一个长度为$n$的数组$a$,取$k$个各不相同的连续区间$[l,r]$ 使得这些区间所有元素的异或和的和最大,求这个最大值
($ 1 \le n \le 5 \times 10^5,1\le k \le min(\frac{n(n-1)}{2},2\times 10^5),0\le a_i \le 4294967295$)

感谢@oy的贡献

分析题意,即找出$n$个数中互不相同且异或和最大的前$k$段区间。

用异或前缀和$s[i]$表示$a[1]\oplus a[2]\oplus … \oplus a[i]$。根据异或运算的性质,区间$[l,r]$的异或和即为$s[r] \oplus s[l-1]$。

将$s[1]$到$s[n]$依次插入$01trie$树中,每次找出对于固定的右端点$r$,与$s[r]$异或值最大的$s[l]$。显然,此次操作找到的是固定右端点为$r$时的最大区间异或和。

将每个不同的$r$值所对应的最大区间异或和插入堆中,显然堆顶的元素即为$n$个数中任意区间的最大异或和。取出堆顶元素,并同时得到这是以$r$为右端点的第$1$大区间异或和。向堆中插入以$r$为右端点的第$2$大区间异或和。

在查询以$r$为右端点的第$k$大区间异或和时,只需稍微更改在$01trie$树上查找的方式即可,与主席树查询区间第$k$小数的思想类似。由于需要访问$01trie$树的历史状态,因此$01trie$需要可持久化。

注意:以$r$为右端点的区间异或和一共只有$r$个,因此查询以$r$为右端点的第$r+1$大区间异或和是没有意义的。

另外,在将(1<<d)这样的式子转long long时,不能写成(long long)(1<<d),而是((long long)1<<d)

代码如下:

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
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=500005;
const int DEP=31;
int N,M;
ll ans,s[MAXN];
struct State{
ll val;
int r,k;
bool operator <(const State& rhs) const {return val<rhs.val;}
};
priority_queue<State> q;
struct Node{
int son[2],sum;
}trie[MAXN*(DEP+2)];
int head[MAXN],cnt;
inline void insert(Node c,Node& u,ll val,int d){
u.sum=c.sum+1;
if(d<0) return;
int x=(val>>d)&1;
u.son[!x]=c.son[!x];
insert(trie[c.son[x]],trie[u.son[x]=++cnt],val,d-1);
}
inline ll query(Node u,ll val,int d,int k){
if(d<0) return 0;
int x=(val>>d)&1;
int lsum=trie[u.son[!x]].sum;
if(lsum>=k)
return ((ll)1<<d)+(ll)query(trie[u.son[!x]],val,d-1,k);
return (ll)query(trie[u.son[x]],val,d-1,k-lsum);
}
int main(){
trie[0].son[0]=trie[0].son[1]=trie[0].sum=0;
insert(trie[0],trie[head[0]=++cnt],0,DEP);
scanf("%d%d",&N,&M);
for(register int i=1;i<=N;++i){
ll a;
scanf("%lld",&a);
s[i]=s[i-1]^a;
insert(trie[head[i-1]],trie[head[i]=++cnt],s[i],DEP);
q.push((State){query(trie[head[i-1]],s[i],DEP,1),i,1});
}
for(register int i=1;i<=M;++i){
ans+=q.top().val;
int r=q.top().r,k=q.top().k;
q.pop();
if(k==r) continue;
q.push((State){query(trie[head[r-1]],s[r],DEP,k+1),r,k+1});
}
printf("%lld",ans);
return 0;
}

$p.s.$洛谷评测机有点小慢,不开O2会小概率发生TLE QwQ

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×