codeforces 617 E. XOR and Favorite Number(莫队)

本文详细介绍了莫队算法的应用场景及其在解决特定区间查询问题中的实现细节。通过实例讲解了如何利用莫队算法优化查询过程,降低时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

n个数,m个区间,问区间内有多少对(i,j)a[i]^a[i+1]^......a[j-1]^a[j]==k,k已给出。


解题思路:

离线的查询,用莫队最好了。

第一次写莫队,所以理解还比较浅。

莫队算法的原理是基于由已知[l,r]的答案推到[l,r-1],[l-1, r],[l, r+1], [l+1, r]的答案是o(1)的,所以我们可以先算出一个区间的答案,然后再逐步由此去求出其它区间的答案,步数是两个区间的曼哈顿距离(把区间看成点的话),这样做的话极限数据是会卡tle的。

然后莫队优化的地方就是在于把询问区间按左端点分块了,然后排序,让左端点坐在的块的编号小的在前面,相等的时候让右端点小的在前面。

这样的话,由i区间移动到i+1区间,左端点的移动就肯定不会超过sqrt(n)了

右端点的话,在一个块内是单调递增的,在一个递增区间内r的差距最大为n,且只会出现一次,因为 单调,然后总共有n^0.5块,所以右端点的总更新时间也不会超过o(nsqrt(n)).

    跨越一个块的话,差距也最多是n,且每两个相邻的块只会出现一次,总共有n^0.5块,所以更新的时间复杂度是o(nsqrt(n));


然后这个题具体来讲需要讲一下的时候add函数和del函数了,在代码里讲吧。


代码:

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn=1<<20;
struct p
{
    int l, r, id;
}block[maxn];
int pos[maxn];
LL book[maxn];
int n, m, k;
LL ANS=0;
LL ans[maxn];
int a[maxn];
bool cmp(p a, p b)
{
    if(pos[a.l]==pos[b.l])
    {
        return a.r<b.r;
    }
    else return pos[a.l]<pos[b.l];
}
int L=1, R=0;
void del(int x)
{
    book[a[x]]--;    //book是统计1,i区间值为y的区间有多少个,这里x点删去,就删去对应的值
    ANS-=book[a[x]^k];   //[i,j]异或值为a[j]^a[i],book[a[x]^k]就是询问有多少个a[i]的值为a[x]^k,对应的就是[i,x]值为k的有多少对,也就是x对应的答案了,这里需要减去x对应的答案
}
void add(int x)
{
    ANS+=book[a[x]^k];
    book[a[x]]++;
}
int main()
{
    int i, j;
    cin>>n>>m>>k;
    int sz=sqrt(n);
    for(i=1; i<=n; i++)
    {
        scanf("%d", &a[i]);
        a[i]=a[i]^a[i-1];
        pos[i]=i/sz;
    }
    for(i=1; i<=m; i++)
    {
        scanf("%d%d", &block[i].l, &block[i].r);
        block[i].id=i;
    }
    sort(block+1, block+m+1, cmp);
    book[0]=1;
    for(i=1; i<=m; i++)
    {
        while(L<block[i].l)
        {
            del(L-1);
            L++;
        }
        while(L>block[i].l)
        {
            L--;
            add(L-1);
        }
        while(R>block[i].r)
        {
            del(R);
            R--;
        }
        while(R<block[i].r)
        {
            R++;
            add(R);
        }
        ans[block[i].id]=ANS;
    }
    for(i=1; i<=m; i++)printf("%lld\n", ans[i]);
        
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值