模板
通常来说,满足以下条件的问题
可以使用莫队二次离线优化
对于一个复杂度为 O ( n n p ) O(n\sqrt np) O(nnp), p p p 不是常数的莫队问题优化到复杂度为 O ( n n + n p ) O(n\sqrt n+np) O(nn+np)
对于模板题,显然暴力莫队的做法是 O ( n n ( 14 k ) ) O(n\sqrt n \binom{14}{k}) O(nn(k14))的,显然炸飞
考虑优化,我们用 f ( i , [ l , r ] ) f(i,[l,r]) f(i,[l,r]) 表示在 [ l , r ] [l,r] [l,r] 中有多少个 j j j 满足 popcount ( a i ⊗ a j ) = k \operatorname{popcount}(a_i\otimes a_j)=k popcount(ai⊗aj)=k
那么如果当前区间从 [ l , r ] [l,r] [l,r] 变成 [ l , r + t ] [l,r+t] [l,r+t] 的时候,我们的增量应该是
Δ a n s = ∑ i = r + 1 r + t f ( i , [ l , i − 1 ] ) \Delta ans=\sum_{i=r+1}^{r+t} f(i,[l,i-1]) Δans=i=r+1∑r+tf(i,[l,i−1])
我们发现 f ( i , [ l , r ] ) f(i,[l,r]) f(i,[l,r]) 是满足前缀和性质的,也就是说
f ( i , [ l , r ] ) = f ( i , [ 1 , r ] ) − f ( i , [ 1 , l − 1 ] ) f(i,[l,r])=f(i,[1,r])-f(i,[1,l-1]) f(i,[l,r])=f(i,[1,r])−f(i,[1,l−1])
那么我们这个式子可以转化成
Δ a n s = ∑ i = r + 1 r + t f ( i , [ 1 , i − 1 ] ) − f ( i , [ 1 , l − 1 ] ) \Delta ans=\sum_{i=r+1}^{r+t}f(i,[1,i-1])-f(i,[1,l-1]) Δans=i=r+1∑r+tf(i,[1,i−1])−f(i,[1,l−1])
我们可以预处理出 f ( i , [ 1 , i − 1 ] ) f(i,[1,i-1]) f(i,[1,i−1]) 的前缀和,那么前半部分是可以 O ( 1 ) O(1) O(1) 计算的
对于后半部分,相当于查询 i ∈ [ r + 1 , r + t ] i\in[r+1,r+t] i∈[r+1,r+t] 在 [ 1 , l − 1 ] [1,l-1] [1,l−1] 里面的贡献
我们可以把他开一个链表挂在 l − 1 l-1 l−1 下面,每次暴力计算这个东西的贡献
考虑这个东西的复杂度为什么是对的
首先预处理 f ( i , [ 1 , i − 1 ] ) f(i,[1,i-1]) f(i,[1,i−1]) 的前缀和,我们可以 O ( n ( n k ) ) O(n\binom{n}{k}) O(n(kn)) 解决
后面暴力查找的时候,我们的访问次数就相当于我们莫队的时候两个指针移动的次数,也就是 O ( n n ) O(n\sqrt n) O(nn)
所以总复杂度就是 O ( n n + n ( 14 k ) ) O(n\sqrt n+n\binom{14}{k}) O(nn+n(k14))
当然上面只说了 [ l , r ] [l,r] [l,r] 变成 [ l , r + k ] [l,r+k] [l,r+k] 的情况,实际上还有 3 3 3 种情况讨论,大家自己推一下就好啦
但是还有一个问题,就是我们上面只有一个端点移动了,但是实际上在两次询问之间,左右端点可能都会移动
考虑 [ l , r ] [l,r] [l,r] 变成 [ x , y ] [x,y] [x,y],我们可以默认一个顺序,比如先移动左端点,挂在 r r r 上,然后在移动右端点的时候,左端点已经变成 x x x 了,所以这个时候我们需要把右端点的询问挂在 x − 1 x-1 x−1 上
然后这道题就愉快的解决啦
#include
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
typedef long long ll;
const int N=1e5+5;
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,m,k,sq;
int a[N];
int pop[N],tot;
ll sum[N];
ll f1[N],f2[N];
// f(i,[1,i-1]),f(i,[1,i])
int pos[N];
ll delta[N],out[N];
struct misaka{
int l,r,id;
}q[N];
struct mikoto{
int l,r,id,opt;
};
list<mikoto> T[N];
bool cmp(misaka x,misaka y){
if(pos[x.l]!=pos[y.l])return pos[x.l]<pos[y.l];
else if(pos[x.l]&1)return x.r<y.r;
else return x.r>y.r;
}
int popcount(int x){
int t=0;
while(x){
x-=x&-x;
t++;
}
return t;
}
int main()
{
read(n),read(m),read(k);
Rep(i,1,n)read(a[i]);
Rep(i,1,m)read(q[i].l),read(q[i].r),q[i].id=i;
if(k>14){
Rep(i,1,m)puts("0");
return 0;
}
sq=sqrt(n);
Rep(i,1,n)pos[i]=(i-1)/sq+1;
Rep(i,0,16384)if(popcount(i)==k)pop[++tot]=i;
Rep(i,1,n){
f1[i]=f1[i-1]+sum[a[i]];
Rep(j,1,tot)sum[a[i]^pop[j]]++;
f2[i]=f2[i-1]+sum[a[i]];
}
sort(q+1,q+m+1,cmp);
Rep(i,1,m){
int a=q[i-1].l,b=q[i-1].r;
int x=q[i].l,y=q[i].r;
if(i==1)a=1,b=0;
delta[i]=f1[y]-f1[b]+f2[x-1]-f2[a-1];
if(x<a)T[b].push_back((mikoto){
x,a-1,i,1});
if(x>a)T[b].push_back((mikoto){
a,x-1,i,-1});
if(y<b)T[x-1].push_back((mikoto){
y+1,b,i,1});
if(y>b)T[x-1].push_back((mikoto){
b+1,y,i,-1});
}
memset(sum,0,sizeof(sum));
Rep(i,1,n){
Rep(j,1,tot)sum[a[i]^pop[j]]++;
for(list<mikoto>::iterator it=T[i].begin();it!=T[i].end();it++){
mikoto u=*it;
ll val=0;
Rep(i,u.l,u.r)val+=sum[a[i]];
delta[u.id]+=u.opt*val;
}
}
Rep(i,1,m)delta[i]+=delta[i-1],out[q[i].id]=delta[i];
Rep(i,1,m)printf("%lld\n",out[i]);
return 0;
}