BZOJ 3261 淺談可持久化TRIE樹最大連續異或和
世界真的很大
trie樹貪心求最大異或和大概也就是那麼回事了
但是對於區間的查詢就不是那麼容易的了
考慮主席樹的思想,怎麼得到區間的值域的
這就是可持久化的trie樹
說來容易
指標教做人哪
看題先:
description:
給定一個非負整數序列 {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 最大,輸出最大是多少。
input:
第一行包含兩個整數 N ,M,含義如問題描述所示。
第二行包含 N個非負整數,表示初始的序列 A 。
接下來 M行,每行描述一個操作,格式如題面所述。
output:
假設詢問操作有 T個,則輸出應該有 T行,每行一個整數表示詢問的答案。
首先考慮怎麼把連續的異或和轉化掉
考慮連續一段的數字之和的轉化,除了線段樹之外的高階東西,前(後)綴和這個東西在這種不修改的情況下就要優秀的多了
[L,R]的數字之和可以轉化成sum[R]-sum[L-1]或last[L]-last[R+1],而考慮異或和這種東西,如果一個數連續異或偶數次就是0,而任何數異或0都是本身,除了0自己
所以如果處理一段連續的異或和的話,就可以考慮用異或前(後)綴和來處理了,而且由於有新增的操作,只會影響字尾而不會影響字首,如果使用字尾的話每次修改都需要把前面的字尾全部修改一遍,這是不能接受的
既然能達到一樣的區間處理效果,儘管每次求的是最大的字尾,但我們仍然選擇用字首維護
每次就用X^=sum[n],再用這樣的X在L到R範圍內找一個sum使其異或和最大
這就轉化成了在一堆數裡面選一個數與指定數異或和最大的經典trie樹貪心的問題了
但我們不可能每次詢問都對指定區間建一顆trie樹,這樣詢問的代價就是O(n)的了,這是不能接受的。我們希望能夠預處理出每次詢問的trie樹,而詢問優勢線上的,
可持久化trie樹就應運而生了
考慮可持久化線段樹,主席樹的實現方式,將R的trie樹減去L-1的trie樹,類比主席樹,得到的就應該是L到R區間的所有數得到的trie樹,而這樣的一顆顆trie樹是可以預處理的,而每次在數列末尾加數字時再新建一顆trie樹,就可以動態的維護這樣的一顆顆樹了
大概思想是有了,接下來考慮實現方法
還是要類比主席樹
每一個點都建一顆trie樹,存的是1到這個點的所有數的二進位制的01串,而對於每一個點都重新建一顆這樣的樹,時間是O(n^2)的,不能接受的,而類比主席樹,每一個點對應的樹和前一顆樹不同的地方只有一個數而已,完全沒有必要重新建一棵樹,這樣空間時間上都能省一筆
可持久化trie樹的大概思想好像差不多了
那麼我們現在需要考慮的就只有具體的建樹思路了
對於相鄰的兩棵樹,只有一個數不同而已,一個數在trie上對應的就是一條鏈,建樹時我們沿著這條鏈走,把這條鏈構造進新樹裡,其餘結構全部指向舊樹
就是說新樹只有這一條鏈的部分是新建的,而其他部分全部與舊樹共享,類比主席樹
還剩下查詢操作,關鍵是判斷在當前區間內有沒有trie樹的這條鏈,考慮主席樹是怎麼做的,對每一個節點開一個域cnt,記錄這個節點以下有幾個數,每次按trie貪心的方法查詢時,判斷R樹的節點cnt值減去L-1樹的節點的cnt值為不為0,不為0就說明區間內有這麼一個數可以走
類比主席樹
大概想一下,具體化一下具體的程式碼就可以開始寫的,還是挺好寫的,只能說指標教做人哪
學新東西時還是注意一下細節
首先是指標寫法,防RE的辦法就不說了,很簡單,注意trie樹insert時使用的是遞迴還是while,是有區別的,while需要另設一個指標,不能直接用root跳
還有就是邊界問題,一定要在trie樹裡一開始插入一個0,這很重要,因為如果取1到n為值的話,應該是sum[n]^sum[0],如果trie樹裡沒有0的話,就無法實現這一步操作,我的做法是直接多建一棵樹,往裡面插值0,然後將n棵樹依次往後推一位,這樣第一棵樹的值就是0了,每次查的時候就查L-1到R就行了,不然應該是L-2到R-1才對(這個也很重要,因為L到R選擇一個字尾,是指用1到X的異或和異或上sum[L-1]到sum[R-1],而想要得到包含sum[L-1]和sum[R-1],不包含sum[R]
完整程式碼:
#include<stdio.h>
#include<cstdio>
#include<algorithm>
using namespace std;
struct node
{
int cnt;
node *ch[2];
}pool[20000010],*tail=pool,*root[600010],*null;
int n,m,sum[600010];
char ss[10];
node *newnode()
{
node *nd=++tail;
nd->cnt=0 ;
nd->ch[0]=nd->ch[1]=null;
return nd;
}
void init()
{
null=++tail;
null->cnt=0;
null->ch[0]=null->ch[1]=null;
}
void insert(node *ne,node *&nd,int x)
{
nd=newnode();
node *now=nd;
for(int i=24;i>=0;i--)
{
now->ch[0]=ne->ch[0],now->ch[1]=ne->ch[1];
now->cnt=ne->cnt+1;
int idx=(x&(1<<i))>0;
now->ch[idx]=newnode();
now=now->ch[idx];ne=ne->ch[idx];
}
now->cnt=ne->cnt+1;
}
int query(node *ne,node *nd,int x)
{
int num=0;
for(int i=24;i>=0;i--)
{
int idx=(x&(1<<i))>0;
if(nd->ch[!idx]->cnt-ne->ch[!idx]->cnt)
{
num|=(1<<i);
ne=ne->ch[!idx],nd=nd->ch[!idx];
}
else
ne=ne->ch[idx],nd=nd->ch[idx];
}
return num;
}
int main()
{
init();
scanf("%d%d",&n,&m);
n++;
for(int i=2;i<=n;i++)
{
scanf("%d",&sum[i]);
sum[i]^=sum[i-1];
}
root[0]=newnode();
for(int i=1;i<=n;i++)
insert(root[i-1],root[i],sum[i]);
while(m--)
{
scanf("%s",ss);
if(ss[0]=='A')
{
scanf("%d",&sum[++n]);
sum[n]^=sum[n-1];
insert(root[n-1],root[n],sum[n]);
}
else
{
int L,R,X;
scanf("%d%d%d",&L,&R,&X);
X^=sum[n];
printf("%d\n",query(root[L-1],root[R],X));
}
}
return 0;
}
/*
Whoso pulleth out this sword from this stone and anvil is duly born King of all England
*/
嗯,就是這樣