【BZOJ2741】【FOTILE模拟赛】L 题解

题面:传送门


    这题是真的套路(可能是因为我太弱了)。

    首先,我们可以把连续区间异或和转化为前缀和的形式,令

v a l i   =   A 1   x o r   A 2   x o r   A 3   x o r   . . .   x o r   A i

    则

A i   x o r   A i + 1   x o r   A i + 2   x o r   . . .   x o r   A j   =   v a l i 1   x o r   v a l j

    即把求 [ l , r ] 内的最大连续区间异或和转化为有 r l + 2 个数 v a l l 1   ,   v a l l   ,   . . .   ,   v a l r ,求它们中任意选两个数的最大异或值。

    这时首先考虑如果知道了一个数 x 和一个数列 a i ,算出从 a i 中任意选一个数与 x 的异或值的最大值怎么做。这明显就是把 a i 先暴力插入 t r i e 中,再用 x 遍历一边就行了。那么如果 x 有很多呢?

    为了快速得到任意需要的 t r i e 树,我们考虑可持久化 t r i e ,这也不难写,直接在主席树上改改就行了,这是插入的代码:

void ins(int &x,int v,int dep)
{
    cnt++;
    son[cnt][0]=son[x][0];
    son[cnt][1]=son[x][1];
    siz[cnt]=siz[x]+1;
    x=cnt;
    if(dep<0)return;
    if(v>>dep&1)ins(son[x][1],v,dep-1);else ins(son[x][0],v,dep-1);
}

    那么怎么查询呢?我们记录一个 s i z e 值,每次访问节点时 s i z e 相减,如果 > 0 那么该节点存在,后面直接按照 t r i e 树上操作就行了:

int query(int a,int b,int v,int dep)
{
    if(dep<0)return 0;
    int d=(v>>dep&1);
    if(siz[son[b][d^1]]-siz[son[a][d^1]])return (1<<dep)+query(son[a][d^1],son[b][d^1],v,dep-1);
    else return query(son[a][d],son[b][d],v,dep-1);
}

    单单一棵可持久化 t r i e 还不够,因为这样也只能做到单点查询,看了网上大佬们的题解后,我恍然大悟。考虑分块,令 d p i , j 存储第 i 块的开头的数到第 j 个数的两两最大异或值,则得到转移方程:

d p i , j = m a x ( d p i , j 1 , q u e r y ( i , j , v a l j ) )

    其中 q u e r y ( i , j , v a l j ) 表示第 i 块的开头的数到第 j 个数中任取一个与 v a l j 的最大异或值。

    那么对于询问 [ l 1 , r ] 的两两最大异或值,我们可以先找到开头 > l 1 且离 l 1 最近的块,设这一块的编号为 x ,那么第 l 1 个数到第 x 块的开头的数的个数一定 n ,直接暴力即可,设剩下来的数中的任意一个与第 l 1 到第 r 个数中的任意一个的最大异或值为 w ,则答案为

m a x ( d p x , r , w )

    于是就做完了。

    建立可持久化 t r i e 的时间复杂度为 Θ ( 30 · n ) ,计算 d p 数组的时间复杂度为 Θ ( 30 · n n ) ,最后直接暴力的时间复杂度为 Θ ( 30 · m n ) ,总的时间复杂度: Θ ( 30 · ( n + m ) n )

    全部代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
using namespace std;
int son[500010][2],siz[500010];
int root[12010],cnt;
int dp[120][12010];
int sq;
int n,m;
void ins(int &x,int v,int dep)
{
    cnt++;
    son[cnt][0]=son[x][0];
    son[cnt][1]=son[x][1];
    siz[cnt]=siz[x]+1;
    x=cnt;
    if(dep<0)return;
    if(v>>dep&1)ins(son[x][1],v,dep-1);else ins(son[x][0],v,dep-1);
}
int query(int a,int b,int v,int dep)
{
    if(dep<0)return 0;
    int d=(v>>dep&1);
    if(siz[son[b][d^1]]-siz[son[a][d^1]])return (1<<dep)+query(son[a][d^1],son[b][d^1],v,dep-1);
    else return query(son[a][d],son[b][d],v,dep-1);
}
int val[12010];
int a[12010]; 
int ans;
int main()
{
    scanf("%d%d",&n,&m);
    sq=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        root[i]=root[i-1];
        scanf("%d",a+i);
        val[i]=val[i-1]^a[i];
        ins(root[i],val[i],30);
    }
    for(int i=1;i<=(n+sq-1)/sq;i++)
    {
        for(int j=(i-1)*sq+1;j<=n;j++)
        {
            dp[i][j]=max(dp[i][j-1],query(root[(i-1)*sq],root[j],val[j],30));
        }
    }
    while(m--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        x=(x%n+ans%n)%n+1;
        y=(y%n+ans%n)%n+1;
        if(x>y)swap(x,y);
        x--;
        int px=(x+sq-1)/sq,py=(y+sq-1)/sq;
        ans=0;
        for(int i=px+1;i<=py;i++)ans=max(ans,dp[i][y]);
        for(int i=x;i<=y && i<=px*sq;i++)ans=max(ans,query(root[x],root[y],val[i],30));
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42112677/article/details/80428154