Codeforces Round #340 (Div. 2) E XOR and Favorite Number

XOR and Favorite Number

这把CF明显题风不太对,前四道一眼题,最后来个模板题。不过正好给我科普了莫队这个姿势。莫队其实更像是一种思想,而不是一个具体的算法。总结下,它能处理的问题是无修改的区间查询,方法是按总长度的平方根分块,然后将询问排序(先按左端点所属块,再按右端点),离线求解。

这道题,先预处理前缀异或结果,记为 prefix [l,r] 的答案就是 prefix[l1] 异或 prefix[r] 。用莫队的时候,就是将左端点固定在块内(当然每次块内移动还是要花 O(n0.5) 的时间去处理的),右端点不断往后推。总复杂度 O(n1.5)

#include <bits/stdc++.h>

using namespace std;

#define ll long long

int n,m,k;
int a[100010];
int prefix[100010];

int cnt[1<<20];

struct query{
    int l;
    int r;
    int block;
    int id;
    bool operator<(const query &other)const{
        if(block!=other.block){
            return block<other.block;
        }
        return r<other.r;
    }
}qs[100010];

ll ans [100010];

int main(){ 
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        prefix[i] = prefix[i-1]^a[i];
    }

    int SQRT = sqrt(n);

    for(int i=0;i<m;i++){
        scanf("%d%d",&qs[i].l,&qs[i].r);
        qs[i].block = qs[i].l/SQRT;
        qs[i].id = i;
    }

    sort(qs,qs+m);

    int preL;
    int preR;

    ll tmp = 0;
    for(int q=0;q<m;q++){
        int L = qs[q].l;
        int R = qs[q].r;

        if(q==0 || qs[q-1].block!=qs[q].block || L>preR){   //新块或者该区间和上个区间没有交集 
            memset(cnt,0,sizeof(cnt));
            preL=L;
            preR=L-1;
            tmp=0;
            cnt[prefix[qs[q].l-1]]++;
        }else{
            //块内调整左边,注意代码顺序. 
            if(L>preL){
                for(int i=preL;i<L;i++){
                    cnt[prefix[i-1]]--;
                    tmp-=cnt[ prefix[i-1]^k ];
                }
            }else if(L<preL){
                for(int i=preL-1;i>=L;i--){
                    tmp+=cnt[ prefix[i-1]^k ];
                    cnt[prefix[i-1]]++;
                }
            }
            preL=L;
        }

        //右边界往后推 
        for(int i=preR+1;i<=R;i++){
            tmp+=cnt[ prefix[i]^k ];
            cnt[prefix[i]]++;
        }

        preR=R;
        ans[qs[q].id] = tmp;
    }

    for(int i=0;i<m;i++){
        printf("%I64d\n",ans[i]);
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值