qwb VS 去污棒 可持續化01字典樹

Problem I: qwb VS 去污棒
Time Limit: 2 Sec Memory Limit: 256 MB
Submit: 95 Solved: 36
[Submit][Status][Web Board]
Description

qwb表白學姐失敗後,鬱鬱寡歡,整天坐在太陽底下賞月。在外人看來,他每天自言自語,其實他在和自己的影子“去污棒”聊天。
去污棒和qwb互相出題考驗對方,去污棒問了qwb這樣一個問題:
現已知一個有n個正整數的序列a[1],a[2]…a[n],接下來有m個操作

操作一共有兩種:

1.在序列末尾添加一個數x。
2.查詢suf[p] xor x的最大值,其中xor是異或 ,l<=p<=r,
suf[t]表示從t開始的後綴的異或和,即suf[t]=a[t] xor a[t+1] xor …xor a[len],len爲序列長度。

Input

第一行一個整數T(<=5),表示一共有T組數據。

每組數據第一行兩個整數n(<=200000),m(<=200000),意義如上所述。
隨後一行有n個數,表示初始序列。
隨後m行,每行表示一個操作。
操作有兩種,1: x 表示在末尾添加一個x,2: l r x表示查詢suf[p] xor x的最大值,其中l<= p <= r,
所有數及x不超過224 且保證所有操作合法。

Output

每組測試數據的第一行輸出”Case x:”,x爲數據組數的標號,從1開始。

接下來,對每個操作2輸出一行答案。

Sample Input
1
5 5
1 2 3 4 5
2 1 3 4
1 10
1 7
2 4 4 5
2 1 5 19

Sample Output
Case 1:
6
9
31

HINT

這種涉及到可持續的增添元素而且頻繁的求最值的,用離線會比較方便。然後如果學過trie樹來處理數的異或的話,會知道,數的異或最大值,只要往異或的相反方向走就好。

對於這種持續刪減修改,明顯是可持續結構,可以保留上一個邊界的值。
每個結點保存的是一個後綴和 然後利用 x^(y^k)=(x^y)^k 來得到結果。 x指的是 p[i].x y指的是後綴和。但是對於後面的操作1(在末尾增加元素),後綴和也加了進去,所以需要用異或刪掉 這個是k。

可持續化就是有這麼一個優點,你能得到所有的區間變化的情況。
而且從中選出最優的
還是太難,即使明白原理也不想不到。

#include <bits/stdc++.h>
using namespace std;
const int N=400010;

struct Trie
{
    int nxt[2];
    int cnt;
};

Trie L[N*27];
int tot,root[N];
int suf[N],arr[N];
int Ans[N];
int n,m;

struct node
{
    int ops;
    int l,r,x;
}p[N];

void init()
{
    memset(L,0,sizeof(L));
    tot=0;
}

void update(int &cur,int ori,int step,int n,int v)
{
    cur=tot++;
    L[cur]=L[ori];
    L[cur].cnt+=v;
    if(step<0) return ;
    int t=(n>>step)&1;
    update(L[cur].nxt[t],L[ori].nxt[t],step-1,n,v);
}

int Find(int S,int E,int step,int n)
{
    if(step<0) return 0;
    int t=(n>>step)&1;
    if(L[L[E].nxt[t^1]].cnt-L[L[S].nxt[t^1]].cnt>0)
    {
        return (1<<step)+Find(L[S].nxt[t^1],L[E].nxt[t^1],step-1,n);
    }
    else return Find(L[S].nxt[t],L[E].nxt[t],step-1,n);
}

int main()
{
    int t;
    scanf("%d",&t);
    for(int cc=1;cc<=t;cc++){
        init();
        scanf("%d%d",&n,&m);
        tot=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&arr[i]);
        }
        for(int i=1;i<=m;i++)
        {
            int op,x;
            scanf("%d",&p[i].ops);
            if(p[i].ops==1)
            {
                scanf("%d",&p[i].x);
                arr[++n]=p[i].x;
            }
            else if(p[i].ops==2)
            {
                scanf("%d%d%d",&p[i].l,&p[i].r,&p[i].x);
            }
        }
        suf[n+1]=0;
        for(int i=n;i>=1;i--)
        {
            suf[i]=suf[i+1]^arr[i];
            update(root[i],root[i+1],25,suf[i],1);
        }
        int last=0;
        int sz=0;
        printf("Case %d:\n", cc);

        for(int i=m;i>=1;i--)
        {
            if(p[i].ops==1)
            {
                last^=p[i].x;
            }
            else
            {
                int one=last^p[i].x;
                Ans[++sz]=Find(root[p[i].r+1],root[p[i].l],25,one);//可以得到 l到r 的所有後綴和異或值 情況。然後從中選出 
                //異或one的 最大情況
            }
        }
        for(int i=sz;i>=1;i--)
        {
            printf("%d\n",Ans[i]);
        }
    }
}
發佈了60 篇原創文章 · 獲贊 4 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章