题面
https://www.luogu.com.cn/problem/P5283
大意是选择k个不同的连续区间,使区间异或和的总和最大
分析
求异或和,可以先对前缀异或和建一棵trie树
固定端点在trie中找与之异或值最大的01串,将异或值和以该端点为右端点的异或值排名加入堆
每次从堆中取出最大异或值并更新以该端点为右端点的区间异或值
为了跳过对于该端点已经选择过的01串,要在trie上找到大小排名为排名+1的(类似线段树二分)01串加入堆
注意细节
代码
#include <iostream> #include <cstdio> #include <queue> using namespace std; typedef long long ll; const ll Inf=1ll<<31; const int N=5e5+10; int t[32*N][2],cnt,sz[32*N]; int n,k; ll s[N],lans; struct Pack { int id,times; ll val; friend bool operator < (Pack a,Pack b) {return a.val<b.val;} }; priority_queue<Pack> q; void Insert(ll s) { int x=0; for (ll i=Inf;i;i>>=1) { bool wch=(s&i)>0; if (!t[x][wch]) t[x][wch]=++cnt; x=t[x][wch];sz[x]++; } } ll Get_Ans(ll s,int met) { int x=0;ll ans=0; for (ll i=Inf;i;i>>=1) { bool wch=(s&i)>0; if (met<=sz[t[x][wch^1]]) ans+=i,x=t[x][wch^1]; else met-=sz[t[x][wch^1]],x=t[x][wch]; } return ans; } int main() { scanf("%d%d",&n,&k); for (int i=1;i<=n;i++) scanf("%lld",&s[i]),s[i]^=s[i-1],Insert(s[i]);Insert(0); for (int i=0;i<=n;i++) q.push((Pack){i,1,Get_Ans(s[i],1)}); for (int i=1;i<=2*k;i++) { Pack temp=q.top();q.pop(); lans+=temp.val; if (temp.times<n) q.push((Pack){temp.id,temp.times+1,Get_Ans(s[temp.id],temp.times+1)}); } printf("%lld",lans/2ll); }