BZOJ 2741 【FOTILE模拟赛】

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2741

题意:给出一个数列A,每次询问区间[L,R] 抑或和最大连续子段。

思路:(1)首先记录前缀抑或和;

(2)分块。设块数为cnt,用f[i][j]表示从第i块开始向右到位置j的最大值;

(3)对于查询[L,R],设x为L之后第一个块的开始,那么[x,R]我们已经得到,对于[L,x-1]暴力即可。

 


struct node
{
    int last;
    node *next[2];
};


node tree[N*32],*root[N];
int tot;
int f[100][N],a[N],n,m,size,pl[N],pr[N],cnt;




void build(int k,int x)
{
    node *p=root[k]=&tree[++tot];
    node *q=root[k-1];
    p->last=k;
    int i,t;
    for(i=30;i>=0;i--)
    {
        t=(x>>i)&1;
        if(q)
        {
            p->next[t^1]=q->next[t^1];
            q=q->next[t];
        }
        p->next[t]=&tree[++tot];
        p=p->next[t];
        p->last=k;
    }
}




int get(int L,int R,int x)
{
    int ans=0,i,t;
    node *p=root[R];
    for(i=30;i>=0;i--)
    {
        t=(x>>i)&1;
        if(!p->next[t^1]) p=p->next[t];
        else
        {
            if(p->next[t^1]->last>=L) p=p->next[t^1],ans|=1<<i;
            else p=p->next[t];
        }
    }
    return ans;
}


int cal(int L,int R)
{
    L--;
    int i,p,ans=0;
    for(p=1;p<=cnt;p++) if(pl[p]>=L) break;
    if(p>cnt||pl[p]>=R)
    {
        for(i=L;i<R;i++) upMax(ans,get(i+2,R+1,a[i]));
    }
    else
    {
        ans=f[p][R];
        for(i=L;i<pl[p];i++) upMax(ans,get(i+2,R+1,a[i]));
    }
    return ans;
}


int main()
{
    RD(n,m);
    int i,j;
    FOR1(i,n) RD(a[i]),a[i]^=a[i-1];
    size=max(n/sqrt(1.0*m),1.0);
    for(i=0;i<=n;i+=size)
    {
        cnt++;
        pl[cnt]=i;
        pr[cnt]=min(i+size-1,n);
    }
    root[0]=&tree[1]; tot=1;
    for(i=0;i<=n;i++) build(i+1,a[i]);
    for(i=1;i<=cnt;i++) for(j=pl[i]+1;j<=n;j++)
    {
        f[i][j]=max(f[i][j-1],get(pl[i]+1,j,a[j]));
    }
    int ans=0,L,R;
    while(m--)
    {
        RD(L,R);
        L=(L+(i64)ans)%n+1;
        R=(R+(i64)ans)%n+1;
        if(L>R) swap(L,R);
        ans=cal(L,R);
        PR(ans);
    }
}

你可能感兴趣的:(ZOJ)