[BZOJ](5301)异或序列 ---- 莫队算法

版权声明:本文为博主原创文章,转载请预先通知博主(〃'▽'〃)。 https://blog.csdn.net/m0_37624640/article/details/81534826

题目链接

做法:知道异或也像加法一样满足前缀和。

即a[l]^a[l+1]^a[l+2]^a[l+3]……^a[r] = a[r]^a[l-1]

我们设a[l]^a[l+1]^a[l+2]^a[l+3]……^a[r] = k,那么k =  a[r]^a[l-1]

异或还满足以下性质:

k =  a[r]^a[l-1]

a[r] = k^a[l-1]

a[l-1] = k^a[r]

由于这道题是离线查询,所以我们可以使用莫队算法。

不过要注意,由于前缀和更新的是l-1和r ,所以我们一开始输入的时候要处理

 

AC代码:

#include<bits/stdc++.h>
#define rep(i,s,t) for(int i = (int)(s); i <= (int)(t); i++)
#define rev(i,t,s) for(int i = (int)(t); i >= (int)(s); i--)
#define pb push_back
#define sz(x) (int)(x).size()
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
const int maxm = 1e6+5;
struct query{
    int l,r,id;
};
query q[maxn];
int block,curR,curL;
int n,m,k;
int sum[maxn];//异或前缀和
int cnt[maxm];//cnt[i]用来保存异或前缀和为i的数量
int ans[maxn];//用来存储每次查询的答案
int now;//用来保存计算的答案
bool cmp(query a,query b)
{
    return a.l/block == b.l/block?a.r<b.r:a.l<b.l;
}
void add(int i)
{
    cnt[i]++;
    now+=cnt[i^k];
}
void del(int i)
{
    now-=cnt[i^k];
    cnt[i]--;
}
int main()
{
    #ifdef  LOCAL_FILE
    freopen("in.txt","r",stdin);
    #endif // LOCAL_FILE
//    ios_base::sync_with_stdio(0);
//    cin.tie(0),cout.tie(0);
    scanf("%d %d %d",&n,&m,&k);
    block = (int)sqrt(n);//块的大小
    for(int i=1;i<=n;i++)
    {
        int d;
        scanf("%d",&d);
        sum[i] = sum[i-1]^d;//异或前缀和
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d %d",&q[i].l,&q[i].r);
        q[i].l--;//因为计算答案用到了前缀和的缘故(区间[l,r],k = s[r]^s[l-1],所以这里使l-1,求得的才是正确的答案)
        q[i].id = i;
    }
    sort(q+1,q+1+n,cmp);
    curL = 1;
    curR = 0;
    for(int i=1;i<=m;i++)
    {
        while(curR<q[i].r) add(sum[++curR]);
        while(curR>q[i].r) del(sum[curR--]);
        while(curL<q[i].l) del(sum[curL++]);
        while(curL>q[i].l) add(sum[--curL]);
        ans[q[i].id] = now;
    }
    for(int i=1;i<=m;i++)
        printf("%d\n",ans[i]);
    return 0;
}

 

阅读更多

没有更多推荐了,返回首页