bzoj3261 最大異或和(可持久化Tire樹)
阿新 • • 發佈:2019-02-04
題目
傳送門
給定一個非負整數序列{a},初始長度為N。有M個操作,有以下兩種操作型別:
1、A x:新增操作,表示在序列末尾新增一個數x,序列的長度N+1。
2、Q l r x:詢問操作,你需要找到一個位置p,滿足l<=p<=r,使得:a[p] xor a[p+1] xor … xor a[N] xor x 最大,輸出最大是多少。
題解
考慮字首和
sum[i]表示1.2….i的字首的異或和,那麼題目讓求的就是sum[i]^sum[n]^x,選取一個i求其最大值.
每次就用X^=sum[n],再用這樣的X在L到R範圍內找一個sum使其異或和最大
這就轉化成了在一堆數裡面選一個數與指定數異或和最大的經典trie樹貪心的問題了
那麼我們對於每一個新加的節點,都建立一棵tire樹是不可以被接受的,類比上面可持久化線段樹,主席樹的思想,將sum[i]轉化為二進位制數,然後建立可持久化trie樹,根據(sum[n]^x)在tire樹上查詢;貪心:儘量使異或和為1,不行的話為0;
對於相鄰的兩棵樹,只有一個數不同而已,一個數在trie上對應的就是一條鏈,建樹時我們沿著這條鏈走,把這條鏈構造進新樹裡,其餘結構全部指向舊樹
就是說新樹只有這一條鏈的部分是新建的,而其他部分全部與舊樹共享,類比主席樹
程式碼
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=600005;
const int sz=25;
int ans,num,n,m,x,y,k,cnt,tot,root[maxn],sum[maxn*30],ch[maxn*30][2];//tot異或和 ch[][]左右孩子
void insert(int &now,int x,int dep)//now根 x當前的異或和,dep深度
{
sum[++num]=sum[now]+1;
ch[num][0]=ch[now][0]; ch[num][1]=ch[now][1];
now=num;
if (dep==-1) return;
int k=(x>>dep)&1;//x的第dep位是否為1;區分左右
if (!k) insert(ch[now][0],x,dep-1);
else insert(ch[now][1],x,dep-1);
}
void ques(int L,int R,int x,int dep)
{
if (dep==-1) return;
int k=(x>>dep)&1;
if (sum[ch[R][k^1]]-sum[ch[L][k^1]]>0)
{
ans|=1<<dep;//或操作有加的一點意思(自行參悟)
ques(ch[L][k^1],ch[R][k^1],x,dep-1);
}
else ques(ch[L][k],ch[R][k],x,dep-1);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1; i<=n; i++)
{
scanf("%d",&x);
tot^=x; root[i]=root[i-1];
insert(root[i],tot,sz-1);
}
cnt=n;
for (int i=1;i<=m;++i)
{
char opt=getchar();
while (opt!='A'&&opt!='Q') opt=getchar();
if (opt=='A')
{
scanf("%d",&x);
tot^=x; root[++cnt]=root[cnt-1];
insert(root[cnt],tot,sz-1);
}
else
{
int l,r;
scanf("%d%d%d",&l,&r,&x);
ans=0;
--l,--r;l=max(l,0);r=max(r,0);
ques(root[l-1],root[r],tot^x,sz-1);
if (l==0) ans=max(ans,tot^x);
printf("%d\n",ans);
}
}
return 0;
}
總結
與異或有關的應該聯想到Tire樹
可持久化資料結構的應用