[Trie][堆]luogu P5283 [十二省联考2019]异或粽子

题面

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);
}
View Code
在日渐沉没的世界里,我发现了你。
原文地址:https://www.cnblogs.com/mastervan/p/14543258.html