正题
题目链接:https://www.luogu.com.cn/problem/P4887
题目大意
给出一个长度为
n
n
n的序列
a
a
a。
m
m
m次询问
[
l
,
r
]
[l,r]
[l,r]求有多少个
l
≤
i
<
j
≤
r
l\leq i< j\leq r
l≤i<j≤r满足
a
i
?
x
o
r
?
a
j
a_i\ xor\ a_j
ai??xor?aj?二进制下恰好有
k
k
k个
1
1
1。
1
≤
n
,
q
≤
1
0
5
,
0
≤
a
i
,
k
<
2
14
1\leq n,q\leq 10^5,0\leq a_i,k<2^{14}
1≤n,q≤105,0≤ai?,k<214
解题思路
记
f
(
x
,
i
)
f(x,i)
f(x,i)表示
1
~
i
1\sim i
1~i中有多少个
a
a
a和
a
x
a_x
ax?满足条件,假设我们已经知道了区间
[
l
,
r
]
[l,r]
[l,r]的答案,以
[
l
,
r
]
→
[
l
,
r
+
1
]
[l,r]\rightarrow [l,r+1]
[l,r]→[l,r+1]为例,答案会增加
f
(
r
+
1
,
r
)
?
f
(
r
+
1
,
l
?
1
)
f(r+1,r)-f(r+1,l-1)
f(r+1,r)?f(r+1,l?1),
f
(
r
+
1
,
r
)
f(r+1,r)
f(r+1,r)我们可以直接预处理出每个。
至于
f
(
r
+
1
,
l
?
1
)
f(r+1,l-1)
f(r+1,l?1)我们发现我们的桶和
f
(
x
,
i
)
f(x,i)
f(x,i)中的
i
i
i也就是
l
?
1
l-1
l?1有关,在
r
r
r移动的过程中
l
?
1
l-1
l?1是不变的,所以我们可以将
r
→
r
+
k
r\rightarrow r+k
r→r+k这个过程离线下来,在指针枚举
l
?
1
l-1
l?1时统一处理。
其他指针的移动一样处理即可。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const ll N=1e5+10,L=1<<14,T=316;
struct node{
ll l,r,id;
}q[N];
ll n,m,k,c[L],v[L],pre[N],a[N],s[N],ans[N];
vector<int> b;
vector<node> u[N];
bool cmp(node x,node y)
{return (x.l/T==y.l/T)?(x.r<y.r):(x.l/T<y.l/T);}
signed main()
{
scanf("%lld%lld%lld",&n,&m,&k);
for(ll i=1;i<L;i++)c[i]=c[i-(i&-i)]+1;
for(ll i=0;i<L;i++)
if(c[i]==k)b.push_back(i);
if(k>14){
for(ll i=1;i<=m;i++)
puts("0");
return 0;
}
for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);
for(ll i=1;i<=n;i++){
pre[i]=v[a[i]];
for(ll j=0;j<b.size();j++)
v[a[i]^b[j]]++;
}
for(ll i=1,l,r;i<=m;i++){
scanf("%lld%lld",&q[i].l,&q[i].r);
q[i].id=i;
}
sort(q+1,q+1+m,cmp);
ll l=1,r=0;
for(ll i=1;i<=m;i++){
if(l<q[i].l)u[r].push_back((node){l,q[i].l-1,-i});
while(l<q[i].l)s[i]+=pre[l],l++;
if(l>q[i].l)u[r].push_back((node){q[i].l,l-1,i});
while(l>q[i].l)l--,s[i]-=pre[l];
if(r<q[i].r)u[l-1].push_back((node){r+1,q[i].r,-i});
while(r<q[i].r)r++,s[i]+=pre[r];
if(r>q[i].r)u[l-1].push_back((node){q[i].r+1,r,i});
while(r>q[i].r)s[i]-=pre[r],r--;
}
memset(v,0,sizeof(v));
for(ll i=1;i<=n;i++){
for(ll j=0;j<b.size();j++)
v[a[i]^b[j]]++;
for(ll j=0;j<u[i].size();j++){
for(ll x=u[i][j].l;x<=u[i][j].r;x++){
ll tmp=v[a[x]];
if(k==0&&x<=i)tmp--;
if(u[i][j].id>0)s[u[i][j].id]+=tmp;
else s[-u[i][j].id]-=tmp;
}
}
}
for(ll i=1;i<=m;i++)
s[i]+=s[i-1],ans[q[i].id]=s[i];
for(ll i=1;i<=m;i++)
printf("%lld\n",ans[i]);
return 0;
}
|