传送门
题解
有的题目本身可以用莫队来做,但是更新或者查询复杂度较高,导致直接使用莫队在这些问题上的表现不算优秀。
在询问的东西可以进行前后缀差分,且存在一个询问复杂度较低的做法的时候,可以考虑这个trick:莫队二次离线。stO %%% lxl %%% Orz
考虑莫队的实际操作过程中我们每一步在干什么,其实算的就是排序后的上一个询问和这一个询问的答案之间的差量。
现在我们考虑计算出所有差量之后来一个前缀和就可以得到所有询问的答案了。
于是我们要考虑其实就是四种端点平移的时候带来的贡献。
以左端点左移为例,剩下的三种直接类比考虑即可。
当前区间为 [ l , r ] [l,r] [l,r],需要把左端点平移到 n l nl nl,更新答案。
设 f ( i , [ l , r ] ) f(i,[l,r]) f(i,[l,r])表示 [ l , r ] [l,r] [l,r]区间中的信息与点 i i i的信息对答案的贡献。
很容易地,我们发现在平移过程中新增的答案就是 ∑ i = n l l − 1 f ( i , [ i + 1 , r ] ) \sum\limits_{i=nl}^{l-1}f(i,[i+1,r]) i=nl∑l−1f(i,[i+1,r])
前面说了要求能够差分,即任何时候, f f f满足 f ( i , [ l , n ] ) − f ( i , [ r + 1 , n ] ) = f ( i , [ l , r ] ) f(i,[l,n])-f(i,[r+1,n])=f(i,[l,r]) f(i,[l,n])−f(i,[r+1,n])=f(i,[l,r])
于是我们发现现在只有两种询问了,分别是 f ( i , [ i + 1 , n ] ) f(i,[i+1,n]) f(i,[i+1,n])和 f ( i , [ r + 1 , n ] ) f(i,[r+1,n]) f(i,[r+1,n])
对于第一种,我们直接从后往前扫描线的同时记录一下后缀和,然后加加减减即可。
对于第二种,我们在扫描线从后面扫到 r + 1 r+1 r+1的时候暴力枚举 i = n l , n l + 1 , … , l − 1 i=nl,nl+1,\dots,l-1 i=nl,nl+1,…,l−1计算即可,由于按照莫队的策略进行了排序,这里的均摊复杂度不会过高。
于是这样一类问题就可以利用扫描线+莫队解决了。
对于上面的洛谷版题,预处理所有有 k k k位 1 1 1的数,然后利用上面的套路在扫描线的同时处理就行了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
namespace IO{
inline char gc(){
static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>T get(){
char c;T num;while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=((num+(num<<2))<<1)+(c^48);
return num;
}inline int gi(){return get<int>();}
}
using namespace IO;
using std::cerr;
using std::cout;
cs int N=1e5+7;
int n,m,k,B;int blk[N];
int bin[16395],bit[16395],ct;
struct Query{int l,r,i;}q[N];
struct Qry{int ql,qr,i,t;};
struct Qsm{int i,t;};
std::vector<Qry> pr[N],sf[N];
std::vector<Qsm> Pr[N],Sf[N];
int a[N];ll ans[N],sum;
void ins(int a){
for(int re i=1;i<=ct;++i)++bin[a^bit[i]];
}
void work(){
n=gi(),m=gi(),k=gi();if(!m)return ;
for(int re i=1;i<=n;++i)a[i]=gi();
for(int re i=0;i<16384;++i){
bin[i]=bin[i>>1]+(i&1);
if(bin[i]==k)bit[++ct]=i;
}
for(int re i=1;i<=m;++i)q[i].l=gi(),q[i].r=gi(),q[i].i=i;
B=(n/sqrt(m)+5)*0.5;for(int re i=1;i<=n;++i)blk[i]=(i-1)/B+1;
std::sort(q+1,q+m+1,[](cs Query &a,cs Query &b){
return blk[a.l]!=blk[b.l]?blk[a.l]<blk[b.l]:(a.r!=b.r&&((blk[a.l]&1)^(a.r>b.r)));
});
for(int re i=1,l=1,r=0;i<=n;++i){
int nl=q[i].l,nr=q[i].r,id=q[i].i;
if(nl!=l){
Sf[nl].push_back({id,1});
Sf[l].push_back({id,-1});
}
if(nr!=r){
Pr[nr].push_back({id,1});
Pr[r].push_back({id,-1});
}
if(nl<l)sf[r+1].push_back({nl,l-1,id,-1}),l=nl;
if(nr>r)pr[l-1].push_back({r+1,nr,id,-1}),r=nr;
if(nl>l)sf[r+1].push_back({l,nl-1,id,1}),l=nl;
if(nr<r)pr[l-1].push_back({nr+1,r,id,1}),r=nr;
}
memset(bin,0,sizeof bin),sum=0;
for(int re i=1;i<=n;++i){
sum+=bin[a[i]];ins(a[i]);
for(auto t:Pr[i])ans[t.i]+=sum*t.t;
for(auto t:pr[i])for(int re j=t.ql;j<=t.qr;++j)ans[t.i]+=t.t*bin[a[j]];
}
memset(bin,0,sizeof bin),sum=0;
for(int re i=n;i;--i){
sum+=bin[a[i]];ins(a[i]);
for(auto t:Sf[i])ans[t.i]+=sum*t.t;
for(auto t:sf[i])for(int re j=t.ql;j<=t.qr;++j)ans[t.i]+=t.t*bin[a[j]];
}
for(int re i=2;i<=m;++i)ans[q[i].i]+=ans[q[i-1].i];
for(int re i=1;i<=m;++i){
cout<<ans[i]<<"\n";
}
}
void file(){
#ifdef zxyoi
freopen("blocks.in","r",stdin);
#endif
}
signed main(){file();work();return 0;}