【BZOJ2741】L-分塊+可持久化trie

測試地址:L
做法:本題需要用到分塊+可持久化trie。
我們知道,一個連續異或和實際上就等於兩個前綴異或和的異或,因此我們求出前綴異或和,轉換成一個新的問題:求區間內兩個數異或的最大值。這個東西直接用數據結構做不好做,而且又強制在線不能用莫隊,因此考慮相似的分塊。
首先對序列分塊,然後對於每一塊,令mx(i,j) 表示第i+1 塊左端點到點j 區間中最大的兩個數的異或值,顯然這個可以用一個trie很快地做出來,時間複雜度爲O(31nn) 。接着對於每一個詢問,如果這個詢問的左右端點在同一塊內,直接用trie暴力算出結果即可,時間複雜度爲O(31n) 。否則,這個區間就會被左端點所在塊的右邊界分割成兩段,其中右邊那段可以直接用前面算出的mx 進行O(1) 計算,而左邊那段可以直接暴力O(31n) 進行計算,關鍵是如何求出一個數在左邊,一個數在右邊的最大異或值呢?只需要對整個序列建一個可持久化trie,然後對於左邊的每一個點,在可持久化trie對應的區間中貪心即可,時間複雜度爲O(31n)
於是我們就以O(31nn) 的時間複雜度完成了這一題。
以下是本人代碼:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,blocksiz,a[12010],pre[12010],suf[12010],block[12010],totp,tot;
int rt[12010]={0},nowrt[12010]={0},mx[120][12010]={0};
int sum[400010]={0},ch[400010][2]={0};

void insert(int v,int last,int x,bool mode)
{
    sum[v]=sum[last]+1;
    for(int i=30;i>=0;i--)
    {
        bool f=(x&(1<<i));
        if (mode) ch[v][f]=++totp;
        else ch[v][f]=++tot;
        ch[v][!f]=ch[last][!f];
        v=ch[v][f];
        last=ch[last][f];
        sum[v]=sum[last]+1;
    }
}

int query(int v,int last,int x)
{
    int ans=0;
    for(int i=30;i>=0;i--)
    {
        ans<<=1;
        bool f=(x&(1<<i));
        f=!f;
        if (sum[ch[v][f]]-sum[ch[last][f]]>0) ans++;
        else f=!f;
        v=ch[v][f];
        last=ch[last][f];
    }
    return ans;
}

void init()
{
    totp=5000;
    for(int i=0;(i+1)*blocksiz<=n;i++)
    {
        tot=0;
        ch[1][0]=ch[1][1]=0;
        nowrt[(i+1)*blocksiz-1]=0;
        for(int j=(i+1)*blocksiz;j<=n;j++)
        {
            nowrt[j]=++tot;
            insert(nowrt[j],nowrt[j-1],suf[j],0);
            mx[i][j]=max(mx[i][j-1],query(nowrt[j],0,suf[j+1]));
        }
    }
    for(int i=1;i<=n;i++)
    {
        rt[i]=++totp;
        insert(rt[i],rt[i-1],pre[i],1);
    }
}

int solve(int L,int R)
{
    int ans=0;
    if (block[L]==block[R])
    {
        tot=0;
        nowrt[L-1]=0;
        for(int i=L;i<=R;i++)
        {
            nowrt[i]=++tot;
            insert(nowrt[i],nowrt[i-1],suf[i],0);
            ans=max(ans,query(nowrt[i],0,suf[i+1]));
        }
    }
    else
    {
        ans=mx[block[L]][R];
        for(int i=L;i<(block[L]+1)*blocksiz;i++)
            ans=max(ans,query(rt[R],rt[(block[L]+1)*blocksiz-1],pre[i-1]));
        tot=0;
        nowrt[L-1]=0;
        for(int i=L;i<(block[L]+1)*blocksiz;i++)
        {
            nowrt[i]=++tot;
            insert(nowrt[i],nowrt[i-1],suf[i],0);
            ans=max(ans,query(nowrt[i],0,suf[i+1]));
        }
    }
    return ans;
}

int main()
{
    scanf("%d%d",&n,&m);
    blocksiz=(int)sqrt(n)+1;
    pre[0]=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        block[i]=i/blocksiz;
        pre[i]=pre[i-1]^a[i];
    }
    suf[n+1]=0;
    for(int i=n;i>=1;i--)
        suf[i]=suf[i+1]^a[i];
    init();

    int lastans=0;
    for(int i=1;i<=m;i++)
    {
        int x,y,l,r;
        scanf("%d%d",&x,&y);
        x=((ll)x+(ll)lastans)%(ll)n+1;
        y=((ll)y+(ll)lastans)%(ll)n+1;
        l=min(x,y),r=max(x,y);
        printf("%d\n",lastans=solve(l,r));
    }

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章