[Cqoi2018]异或序列

本文介绍了一种解决区间异或和查询问题的有效算法。针对一个整数序列,该算法可以快速找出所有满足特定异或和条件的连续子序列。通过预处理前缀和并使用莫队算法进行优化,达到了较高的查询效率。

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

Description

已知一个长度为 n 的整数数列 a[1],a[2],…,a[n] ,给定查询参数 l、r ,问在 [l,r] 区间内,有多少连续子
序列满足异或和等于 k 。
也就是说,对于所有的 x,y (l≤x≤y≤r),能够满足a[x]^a[x+1]^…^a[y]=k的x,y有多少组。
 

Input

输入文件第一行,为3个整数n,m,k。
第二行为空格分开的n个整数,即ai,a2,….an。
接下来m行,每行两个整数lj,rj,表示一次查询。
1≤n,m≤105,O≤k,ai≤105,1≤lj≤rj≤n

Output

输出文件共m行,对应每个查询的计算结果。

Sample Input

4 5 1
1 2 3 1
1 4
1 3
2 3
2 4
4 4

Sample Output

4
2
1
2
1

HINT

 

Source

 
预处理前缀和。
若i-j,异或和为K。
则 sum[i-1]^sum[j]=k。
则用莫队处理,加入sum[i-1] ,每次(O1)处理,ans+=cnt[sum[i-1]^k].
#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
struct quer
{
    int l,r,pos;
}query[maxn];
int block;
bool cmp(quer a,quer b)
{
    if(a.l/block!=b.l/block) return (a.l/block)<(b.l/block);
    return a.r<b.r;
}
int now=0,k;
int ans[maxn];
int sum[maxn];
int cnt[maxn];
void add(int x)
{   cnt[x]++;
    now+=cnt[x^k];
}
void dele(int x)
{   cnt[x]--;
    now-=cnt[x^k];
}
int main()
{
    int n,m,i;
    int a[maxn];
    cin>>n>>m>>k;
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    block=sqrt(n);
    for(i=1;i<=n;i++)
    {
        sum[i]=sum[i-1]^a[i];
    }
    for(i=1;i<=m;i++)
    {
        scanf("%d%d",&query[i].l,&query[i].r);
        query[i].pos=i;
        query[i].l--;
    }
    sort(query+1,query+1+m,cmp);
    int l=1,r=0;
    for(i=1;i<=m;i++)
    {
      while(l<query[i].l)
      {
          dele(sum[l]);
          l++;
      }
      while(l>query[i].l)
      {
          l--;
          add(sum[l]);
      }
      while(r<query[i].r)
      {   r++;
          add(sum[r]);
      }
      while(r>query[i].r)
      {
          dele(sum[r]);
          r--;
      }
      ans[query[i].pos]=now;
    }
    for(i=1;i<=m;i++)
    {
        printf("%d\n",ans[i]);
    }
    return 0;
}

  

转载于:https://www.cnblogs.com/zyf3855923/p/9439237.html

# P4462 [CQOI2018] 异或序列 ## 题目描述 已知一个长度为 $n$ 的整数数列 $a_1,a_2,\dots,a_n$,给定查询参数 $l,r$,问在 $a_l,a_{l+1},\dots,a_r$ 区间内,有多少子区间满足异或和等于 $k$。也就是说,对于所有的 $x,y (l \leq x \leq y \leq r)$,能够满足 $a_x \oplus a_{x+1} \oplus \dots \oplus a_y = k$ 的 $x,y$ 有多少组。 ## 输入格式 输入文件第一行,为 $3$ 个整数 $n,m,k$。 第二行为空格分开的 $n$ 个整数,即 $a_1,a_2,..a_n$。 接下来 $m$ 行,每行两个整数 $l_j,r_j$,表示一次查询。 ## 输出格式 输出文件共 $m$ 行,对应每个查询的计算结果。 ## 输入输出样例 #1 ### 输入 #1 ``` 4 5 1 1 2 3 1 1 4 1 3 2 3 2 4 4 4 ``` ### 输出 #1 ``` 4 2 1 2 1 ``` ## 说明/提示 对于 $30\%$ 的数据,$1 \leq n, m \leq 1000$。 对于 $100\%$ 的数据,$1 \leq n, m \leq 10^5$,$0 \leq k, a_i \leq 10^5$,$1 \leq l_j \leq r_j \leq n$。 #include <bits/stdc++.h> #define ll long long using namespace std; const ll N=2e5; struct node{ ll l,r,f,ans; }b[N+5]; ll n,m,k; ll blk[N+5],lenb; ll a[N+5],pre[N+5]; ll cnt[N+5]; ll lp,rp,s; bool cmp(node l1,node l2){ if(blk[l1.l]==blk[l2.l]) return l1.r<l2.r; return blk[l1.l]<blk[l2.l]; } bool cmpf(node l1,node l2){ return l1.f<l2.f; } void solve(ll l,ll r,ll &ans){ while(lp<l){ s-=cnt[k^pre[lp-1]]; cnt[pre[lp]]--; lp++; } while(lp>l){ lp--; cnt[pre[lp]]++; s+=cnt[k^pre[lp-1]]; } while(rp<r){ rp++; s+=cnt[k^pre[rp]]; cnt[pre[rp]]++; if((pre[lp-1]^pre[rp])==k) s++; } while(rp>r){ s-=cnt[k^pre[rp]]; cnt[pre[rp]]--; if((pre[lp-1]^pre[rp])==k) s--; rp--; } ans=s; return; } int main(){ scanf("%lld%lld%lld",&n,&m,&k); ll sx=1; lenb=(ll)sqrt(n); for(ll i=1;i<=n;i++){ blk[i]=sx; if(sx%lenb==0) sx++; } for(ll i=1;i<=n;i++){ scanf("%lld",&a[i]); pre[i]=(pre[i-1]^a[i]); } for(ll i=1;i<=m;i++){ scanf("%lld%lld",&b[i].l,&b[i].r); } sort(b+1,b+m+1,cmp); lp=rp=cnt[pre[1]]=1; if(k==0) s=1; for(ll i=1;i<=m;i++){ solve(b[i].l,b[i].r,b[i].ans); } sort(b+1,b+m+1,cmpf); for(ll i=1;i<=m;i++) printf("%lld\n",b[i].ans); return 0; } 不改变原来代码写法情况下标出并修正错误
最新发布
07-31
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值