Codeforces 617E XOR and Favorite Number (Round #340 (Div. 2) E题) 莫队算法 + 异或基本性质

题意

  • 给你一个数组和一个数k,然后有m个查询,每次给你l,r,问你[l , r]区间内有多少对(i,j),使得a[i] ^ a[i+1] … ^ a[j] = k

思路

  • pref[i]记录a[0] ^ a[1] ^ … ^a[i]的值
  • 区间[l, r]的解 = 统计(i, j)使得pref[i-1] ^ pref[j] = k的个数。(因为,a ^ b ^ a = b,所以所有小于i的数被以后两次后就相当于没被异或,而i~j则被异或了一次)
  • 然后,为了方便实现,我们换一种说法,相当于统计在[l-1, r]区间内有多少(i, j),使得pref[i] ^ pref[j] = k
  • 当然,因为有m个查询我们不能简单的暴力求解,所以这里用到了莫队算法
  • 这里给两个我自学时参考的文章:
  • http://wenku.baidu.com/link?url=id9fDlyKDaAINJtr6Iq3SYY97VOvec4cdo5QazWreZX3jCNFJU-gRZEreLicE2zoYWjpY4YCe1HmwewpH8bufjeb_aivNOuWcUp0HPlZzCi
  • http://blog.csdn.net/bossup/article/details/39236275
  • 基本想法就是,离线算法,存储所有的查询,然后对查询进行某种方法的排序,之后暴力的去算
  • 但是,这里的暴力也不太一样,是当算出了[l, r]区间的解后,左右移动l 和 r, 来算出[l’ , r’]的解
  • 所以,它要求我们可以很快的从[l, r]的解算出, [l-1, r], [l+1, r], [l, r-1], [l, r+1]的解
  • 这里用cnt数组来辅助,cnt[v] 表示 [l-1, r]区间内有cnt[v]个pref[i] = v的数, 然后我们就可以很快的算出刚才所述的解,具体方法,详见代码,要注意的是更新ans和cnt的顺序是有要求的,需要仔细考虑
  • 最后说个坑点,cnt数组不能开小了。。。因为1e6的数异或之后可不止1e6。。我居然被这个点坑了一个上午。。。

实现

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e6+6;
typedef long long ll;
const int B = 316; //316 < sqrt(1e5)
int a[maxn];
int pref[maxn];
struct Node{
    int l,r,id;
}query[maxn];
int cnt[maxn];
ll Ans[maxn];

bool cmp(Node x, Node y){
    if (x.l / B != y.l / B)
        return x.l < y.l;
    return x.r < y.r;
}

int main(){
    int n,m,k;
    ios::sync_with_stdio(false);
    cin>>n>>m>>k;
    for (int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    for (int i=1;i<=n;i++){
        pref[i] = a[i] ^ pref[i-1];
    }
    for (int i=0;i<m;i++){
        int l,r;
        cin>>query[i].l>>query[i].r;
        query[i].id = i;
    }
    sort(query,query+m,cmp);

    //这里可以让r = 0进行初始化
    int l = 0, r = 1;
    ll ans = a[1] == k ? 1 : 0;
    cnt[pref[1]]++;
    cnt[0]++;
    for (int i=0; i < m; i++){
        while (l < query[i].l - 1){
            cnt[pref[l]]--;
            ans -= (ll)cnt[pref[l] ^ k];
            l++;
        }
        while (l > query[i].l - 1){
            l--;
            ans += (ll)cnt[pref[l] ^ k];
            cnt[pref[l]]++;     
        }

        while (r > query[i].r){
            cnt[pref[r]]--;
            ans -= (ll)cnt[pref[r] ^ k];
            r--;
        }
        while (r < query[i].r){
            r++;
            ans += (ll)cnt[pref[r] ^ k];
            cnt[pref[r]]++;
        }
        Ans[query[i].id] = ans;
    }
    for (int i=0;i<m;i++){
        cout << Ans[i] << endl;
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值