[十二省聯考 2019][堆][可持久化trie樹]異或粽子
題意
小粽是一個喜歡吃粽子的好孩子。今天她在家裏自己做起了粽子。
小粽面前有n種互不相同的粽子餡兒,小粽將它們擺放為了一排,並從左至右編號為1到n。第i種餡兒具有一個非負整數的屬性值ai 。每種餡兒的數量都足夠多,即小粽不會因為缺少原料而做不出想要的粽子。小粽準備用這些餡兒來做出k個粽子。
小粽的做法是:選兩個整數數l,r
,滿足1<=l<=r<=n,將編號在[l,r]範圍內的所有餡兒混合做成一個粽子,所得的粽子的美味度為這些粽子的屬性值的異或和。(異或就是我們常說的
運算,即 C/C++ 中的 ^
運算符或 Pascal 中的 xor
運算符)
小粽想品嘗不同口味的粽子,因此它不希望用同樣的餡兒的集合做出一個以上的粽子。
小粽希望她做出的所有粽子的美味度之和最大。請你幫她求出這個值吧!
輸入格式:
第一行兩個正整數 n, k,表示餡兒的數量,以及小粽打算做出的粽子的數量。
接下來一行為 n 個非負整數,第 i 個數為 ai?,表示第 i 個粽子的屬性值。 對於所有的輸入數據都滿足:1?n?5×1e5, 1?k?min?{ n(n−1)2, 2×1e5}, 0?ai?42949672950。
輸出格式:
輸出一行一個整數,表示小粽可以做出的粽子的美味度之和的最大值。
內存限制:1024 MB 時間限制:2000 ms
題解:
由異或自為逆運算的性質,求出異或前綴和 l~r異或和為a[r]^a[l-1]
這樣問題就轉化為 求a[0]=0, a[0]~a[n] 中任意兩數前k大異或和之和
類似於超級鋼琴
做法一
我們先固定右端點 r,然後在 [0,r−1]查一個數異或 sumr 最大。這個可以用可持久化 01trie 實現。
我們將 n個數放入堆中,每次取出最大的那個狀態。設這個狀態左端點在 [l,r],與 sumx異或起來最大的位置在 k,那麽我們把狀態的左端點分割成[l,k-1]和 [k+1,r]後放入堆中。
時間復雜度 O(nlog?n)。
做法二
註意到這是一個關於三角(ai xor aj,0≤i≤j≤n)的求值,並且有ai xor aj=aj xor ai
可以對每一個i求出第t(初始為1)大的ai xor aj,然後把結果扔到堆裏,每次取堆頂,然後把堆頂對應的i的第t+1大的ai xor aj扔進堆裏.
做法一代碼 註意可持久化trie的邊界問題 因為可能要查找區間[0,r],實際上rt[-1]=0 註意特判
1 #include<cstdio> 2 #include<queue> 3 using namespace std; 4 const int N=5*1e5+15; 5 int n,k; long long a[N],ans; 6 7 struct point{int l,r,pos; long long he,val;}; 8 inline bool operator <(const point a,const point b) {return a.val<b.val;} 9 priority_queue<point> q; 10 11 int rt[N],cnt; 12 struct tree{int ch[2],pos,sum;}t[N*40]; 13 inline void insert(int& now,int las,int pos,long long x) 14 {if(las<0) las=0; else las=rt[las]; 15 16 now=++cnt; int tmp=now; 17 for(int i=31;i>=0;i--) 18 {t[tmp]=t[las]; t[tmp].sum++; 19 long long cmp=((x>>i)&1); 20 tmp=t[tmp].ch[cmp]=++cnt; las=t[las].ch[cmp]; 21 } 22 t[tmp]=t[las]; t[tmp].sum++; t[tmp].pos=pos; 23 } 24 25 inline int ask(int l,int r,long long x) 26 { 27 if(l==-1) l=0; else l=rt[l]; 28 29 for(int i=31;i>=0;i--) 30 {long long cmp=((x>>i)&1); 31 if(t[t[r].ch[cmp^1]].sum>t[t[l].ch[cmp^1]].sum) 32 {l=t[l].ch[cmp^1],r=t[r].ch[cmp^1];} 33 else 34 {l=t[l].ch[cmp],r=t[r].ch[cmp];} 35 } 36 return t[r].pos; 37 } 38 39 inline long long read() 40 {long long re=0; char ch=getchar(); 41 while(!(‘0‘<= ch && ch<=‘9‘)) ch=getchar(); 42 while(‘0‘<= ch && ch<=‘9‘) {re=re*10+ch-‘0‘; ch=getchar();} 43 return re; 44 } 45 int main() 46 { 47 n=read(); k=read(); 48 for(int i=1;i<=n;i++) a[i]=(read()^a[i-1]); 49 50 for(int i=0;i<=n;i++) {insert(rt[i],i-1,i,a[i]);} 51 52 for(int i=1;i<=n;i++) 53 {point s; 54 s.l=0; s.r=i-1; s.he=a[i]; 55 s.pos=ask(s.l-1,rt[s.r],s.he); 56 s.val=(s.he^a[s.pos]); 57 q.push(s); 58 } 59 60 while(k--) 61 {point s,x=q.top(); q.pop(); ans+=x.val; 62 63 s.l=x.l; s.r=x.pos-1; s.he=x.he; 64 if(s.l<=s.r) 65 { s.pos=ask(s.l-1,rt[s.r],s.he); 66 s.val=(s.he^a[s.pos]); 67 q.push(s); 68 } 69 70 s.l=x.pos+1; s.r=x.r; s.he=x.he; 71 if(s.l<=s.r) 72 { s.pos=ask(s.l-1,rt[s.r],s.he); 73 s.val=(s.he^a[s.pos]); 74 q.push(s); 75 } 76 } 77 printf("%lld",ans); 78 79 return 0; 80 }
[十二省聯考 2019][堆][可持久化trie樹]異或粽子