题意简述
给定一个序列(长度 n < = 1 e 5 n<=1e5 n<=1e5,每个数 a i < = 1 e 5 a_i<=1e5 ai<=1e5),和一个常数 k < = 1 e 5 k<=1e5 k<=1e5。还有一些( < = 1 0 5 <=10^5 <=105)个询问 l , r ( 1 < = l , r < = n , k < = 1 0 5 ) l,r(1<=l,r<=n,k<=10^5) l,r(1<=l,r<=n,k<=105),问:有多少 ( x , y ) (x,y) (x,y)满足:
- x x x到 y y y的异或和为 k k k
- l < = x < = y < = r l<=x<=y<=r l<=x<=y<=r
(详细数据呢,以后就不写了,懒得写,也没什么用,我觉得关键还是在思路)
思路
(注:以下
⊕
\oplus
⊕运算表示异或)
首先我们要转化一下异或和的式子。我们处理一个前缀异或值
s
[
]
s[]
s[],
s
[
i
]
s[i]
s[i]表示
[
1
,
i
]
[1,i]
[1,i]的异或和。由于异或的逆运算是异或,那么
[
x
,
y
]
[x,y]
[x,y]的异或和就是
s
[
y
]
⊕
s
[
x
−
1
]
s[y]\oplus s[x-1]
s[y]⊕s[x−1]。
那现在询问就变成了:给定一些
l
,
r
l,r
l,r,问
[
l
−
1
,
r
]
[l-1,r]
[l−1,r]中有多少对数异或值为
k
k
k。
考虑到每个数
<
=
1
0
5
<=10^5
<=105,那么(目测)异或值最大不会超过
3
e
5
3e5
3e5。反正不会
M
L
E
MLE
MLE,开他娘的即珂。然后我们就用莫队分块做这个东西了。设
c
n
t
[
x
]
cnt[x]
cnt[x]表示:当前考虑的区间内有多少个数是
x
x
x。
然后有一个全局变量
a
n
s
ans
ans表示当前的答案。
对于新加入的点
y
y
y:
a
n
s
+
=
c
n
t
[
k
⊕
y
]
ans+=cnt[k\oplus y]
ans+=cnt[k⊕y](因为
y
⊕
(
k
⊕
y
)
=
k
y \oplus (k\oplus y)=k
y⊕(k⊕y)=k,所以对于新加入的点
y
y
y,只要能找到
k
⊕
y
k\oplus y
k⊕y就能和其异或凑出
k
k
k)
c
n
t
[
y
]
+
+
cnt[y]++
cnt[y]++(这个不用解释吧)
删除类似。差不多写即珂。(都到这了代码还不会写,那么也许是因为你对莫队的理解还不够深?如果是的话,珂以考虑参考我的蒟蒻代码)
代码:
// luogu-judger-enable-o2
//开挂的痕迹
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define N 300100
int n,m,k;
int a[N];
int sn;//sn=sqrt(n)
struct node//offline queries
{
int l,r;
int id,ans;
}Q[N];
bool operator<(node a,node b)
{
int ali=(a.l-1)/sn+1,bli=(b.l-1)/sn+1;
if (ali!=bli) return ali<bli;
else return a.r<b.r;
}
bool cmp_id(node a,node b)
{
return a.id<b.id;
}
void R1(int &x)
{
x=0;char c=getchar();int f=1;
while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=(f==1)?x:-x;
}
void Input()
{
R1(n),R1(m),R1(k);
for(int i=1;i<=n;++i)
{
R1(a[i]);a[i]^=a[i-1];
}
sn=sqrt(n+0.05);
for(int i=1;i<=m;++i)
{
int l,r;
R1(l),R1(r);--l;
Q[i]=(node){l,r,i,-1};
}
sort(Q+1,Q+m+1);
}
int cnt[N],nowans;
void Add(int pos)//加入新点pos
{
int val=a[pos];
nowans+=cnt[val^k];
++cnt[val];
}
void Del(int pos)//删除点pos
{
int val=a[pos];
--cnt[val];
nowans-=cnt[val^k];
//这个不是很显然么。。。
}
void Soviet()
{
int l=1,r=0;
for(int i=1;i<=m;++i)
{
while(l<Q[i].l) Del(l),++l;
while(l>Q[i].l) --l,Add(l);
while(r<Q[i].r) ++r,Add(r);
while(r>Q[i].r) Del(r),--r;
//不断滑动区间
Q[i].ans=nowans;//记录答案
}
sort(Q+1,Q+m+1,cmp_id);
for(int i=1;i<=m;++i)
{
printf("%d\n",Q[i].ans);
}
}
void IsMyWife()
{
if (0)
{
freopen("","r",stdin);
freopen("","w",stdout);
}
Input();
Soviet();
}
};
int main()
{
Flandle_Scarlet::IsMyWife();
return 0;
}
博客介绍了洛谷4462与bzoj5301题目,涉及异或序列问题。通过转化异或和公式,使用莫队算法处理询问范围内的异或对数。文章提供了思路解析和代码实现。
908

被折叠的 条评论
为什么被折叠?



