题意:
给定一个序列,问最大的k段连续异或和的代数和。
知识点:
可持久化Trie,堆
解法:
首先异或的一个性质可以把连续子串转化成前缀和的形式维护。
然后看到异或代数和最大,可以想到可持久化Trie。
但是维护的方法又有两种。
第一种也是我一开始想到的,把n个值最大的放到堆中,每次从堆中取出一个元素,更新答案;然后找出这个元素唯一对应的下一个值(暴力跳trie,最多不超过64次)。但是太难了。
第二种就是超级钢琴的做法,在l到r中找到最大的答案为pos点,然后放入l到pos-1和pos-1到r继续更新答案即可。
备注:
这种超级钢琴的做法很值得学习。注意这道题堆里面要维护的是l,r,pos,val,id,id不可以省。
代码:
#include<cstdio> #include<cstring> #include<queue> using namespace std; typedef long long ll; const int maxn=500010; int n,m,tot,bit[35],rt[maxn]; ll ans,sum[maxn]; struct trie { int ch[2],tag,id; }a[maxn*40]; struct data { int l,r; ll val; int pos,id; bool operator <(const data &b)const { return val<b.val; } }; priority_queue<data>q; ll read() { ll x=0; char c=getchar(); while (c<48||c>57) c=getchar(); while (c>=48&&c<=57) x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x; } void div(ll x) { int i; for (i=31;i>=0;i--) { bit[i]=(x&1); x>>=1; } } void insert(int &x,int y,int id) { x=(++tot); a[x].tag=id; int i,u=x; for (i=0;i<=31;i++) { a[u].ch[bit[i]]=(++tot); a[tot].tag=id; if (y) a[u].ch[bit[i]^1]=a[y].ch[bit[i]^1]; u=a[u].ch[bit[i]]; if (y) y=a[y].ch[bit[i]]; } a[u].id=id; } ll query(int l,int r,int &pos) { pos=0; ll res=0; int i,u=rt[r]; for (i=0;i<=31;i++) { if (a[u].ch[bit[i]^1]&&a[a[u].ch[bit[i]^1]].tag>=l) { res|=(1ll<<(31-i)); u=a[u].ch[bit[i]^1]; } else u=a[u].ch[bit[i]]; } pos=a[u].id; return res; } int main() { int i,u; ll x,v; n=read(),m=read(); sum[0]=0; insert(rt[0],0,0); for (i=1;i<=n;i++) { x=read(); sum[i]=sum[i-1]^x; div(sum[i]); v=query(0,i-1,u); q.push((data){0,i-1,v,u,i}); insert(rt[i],rt[i-1],i); } data c; while (m--) { c=q.top(); q.pop(); ans+=c.val; div(sum[c.id]); if (c.l<c.pos) { v=query(c.l,c.pos-1,u); q.push((data){c.l,c.pos-1,v,u,c.id}); } if (c.pos<c.r) { v=query(c.pos+1,c.r,u); q.push((data){c.pos+1,c.r,v,u,c.id}); } } printf("%lld\n",ans); return 0; }
来源:https://www.cnblogs.com/Ronald-MOK1426/p/12298846.html