[BZOJ2741]【FOTILE模拟赛】L(分块+可持久化trie树)

12 篇文章 0 订阅
1 篇文章 0 订阅

题目:

我是超链接

题解:

我们对这个题目转化一下: sum[l1] s u m [ l − 1 ] ^ sum[r] s u m [ r ] ,每个询问就转化为在sum[l-1]到sum[r]之间找两个数,使其异或值最大。
问题的简化:在区间内找一个数,使它与另一个已知数的异或值最大。这个问题可以用可持久化Trie树解决,只需要在Trie树上走相异的节点,可以O(log)的级别求出来
对于现在的问题,我们先分块然后预处理f,f[i][j]表示以i为起点的块到j点异或最大值是多少,那么不难列出 f[i][j]=max(f[i][j1],query((i1)block+1,j,a[j])) f [ i ] [ j ] = m a x ( f [ i ] [ j − 1 ] , q u e r y ( ( i − 1 ) ∗ b l o c k + 1 , j , a [ j ] ) )
然后对于每个询问,找到区间内第一个完整块的起始点x,答案赋值为f[pos[x]][r],然后对于左边剩余的点暴力计算并和答案取max。
预处理效率 O(nn) O ( n n ) 查询效率是 O(mnlog a[i]) O ( m n l o g   a [ i ] )

代码:

#include <cstdio>
#include <cmath>
#include <algorithm>
#define LL long long
using namespace std;
const int sz=30;
const int N=12005;
int ans,size,tot,n,sum[N*60],ch[N*60][2],a[N],root[N],block,m,pos[N],f[120][N];
void insert(int &now,int x,int dep)
{
    sum[++size]=sum[now]+1; ch[size][0]=ch[now][0]; ch[size][1]=ch[now][1];
    now=size;
    if (dep==-1) return;
    int k=(x>>dep)&1;
    if (k) insert(ch[now][1],x,dep-1);
    else insert(ch[now][0],x,dep-1);
}
void query(int l,int r,int x,int dep)
{
    if (dep==-1) return;
    int k=(x>>dep)&1;
    if (sum[ch[r][k^1]]-sum[ch[l][k^1]]>0)
    {
        ans+=(1<<dep);
        query(ch[l][k^1],ch[r][k^1],x,dep-1);
    }else query(ch[l][k],ch[r][k],x,dep-1);
}
int main()
{
    freopen("data.in","r",stdin);
    int q;scanf("%d%d",&n,&q);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]^=a[i-1],root[i]=root[i-1],insert(root[i],a[i],sz);
    block=sqrt(n);
    m=n/block; if (n%block) m++;
    for (int i=1;i<=n;i++) pos[i]=(i-1)/block+1;
    for (int i=1;i<=m;i++)
      for (int j=(i-1)*block+1;j<=n;j++)
      {
        ans=0;query(root[(i-1)*block],root[j],a[j],sz);
        f[i][j]=max(f[i][j-1],ans);
      }
    int answer=0;
    while (q--)
    {
        int l,r,x,y;
        scanf("%d%d",&x,&y);
        l=(((LL)x+answer)%n)+1;
        r=(((LL)y+answer)%n)+1;
        if (l>r) swap(l,r);
        l--;answer=0;
        if (pos[l]==pos[r])
        {
            for (int i=l;i<=r;i++) 
              ans=0,query(root[l-1],root[r],a[i],sz),answer=max(answer,ans);
        }
        else
        {
            answer=f[pos[l]+1][r];
            int up=min(n,pos[l]*block);
            for (int i=l;i<=up;i++) 
              ans=0,query(root[l-1],root[r],a[i],sz),answer=max(answer,ans);
        }
        printf("%d\n",answer);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值