【题目】
原题地址
给定一个序列 a a a,一个非负整数 k k k,和若干个询问 [ l , r ] [l,r] [l,r],问这段区间中满足 a i ⨁ a j 的 二 进 制 中 有 k 个 1 , 且 i < j 的 数 对 数 a_i\bigoplus a_j的二进制中有k个1,且i<j的数对数 ai⨁aj的二进制中有k个1,且i<j的数对数。
n , m ≤ 1 0 5 , a i < 16384 n,m\leq 10^5,a_i<16384 n,m≤105,ai<16384。
【解题思路】
真是一道好(du)题啊!
考虑莫队,我们要求的实际上是一个位置对一个区间的贡献,这个显然是可以差分的。
现在只考虑右端点的移动,那么观察贡献的区间,总是某个位置 i i i对 [ 1 , l − 1 ] [1,l-1] [1,l−1]的贡献以及 i i i对一个前缀 [ 1 , i − 1 ] [1,i-1] [1,i−1]的贡献。
后者我们可以通过预处理得到。
对于前者,观察到右端点的移动总是连续的一段,那么我们可以在 l − 1 l-1 l−1的位置 p u s h _ b a c k push\_back push_back一个区间,表示这段区间对这个前缀产生贡献。
由于区间总长是 n n n\sqrt n nn的,所以我们现在只需要用一个支持 O ( 1 ) O(1) O(1)查询, O ( 3432 ) O(3432) O(3432)插入一个数字的东西就可以计算这部分的贡献,用一个数组就可以了。
左端点的移动类似,就是将对前缀的贡献改为对后缀的贡献即可。
时间复杂度 O ( 3432 ∗ n + n n ) O(3432*n+n\sqrt n) O(3432∗n+nn)
【参考代码】
#include
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
#define one(x) __builtin_popcount(x)
using namespace std;
typedef long long LL;
const int N=1e5+10,M=16384,lim=332;
int n,m,K,cnt;
int a[N],num[M],num2[M],kth[N];//cb1=1~i-1,cb2=1~i
LL ans[N],tans[N],cbl[N],cbr[N];
inline char Getchar()
{
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
bool Isdigit(char c){return (c>='0' && c<='9')?1:0;}
inline int read()
{
int ret=0;char c=Getchar();
while(!Isdigit(c)) c=Getchar();
while(Isdigit(c)) ret=ret*10+(c^48),c=Getchar();
return ret;
}
inline void write(LL x)
{
if(x>9)write(x/10);
putchar(x%10+'0');
}
struct Tquery{int l,r,bl,id;}q[N];
inline bool cmp(const Tquery&A,const Tquery&B)
{
if(A.bl^B.bl) return A.l<B.l;
return (A.bl&1)?A.r<B.r:A.r>B.r;
}
struct Tnode
{
int l,r,op,id;
Tnode(){}
Tnode(int _l,int _r,int _op,int _id):l(_l),r(_r),op(_op),id(_id){}
};
vector<Tnode>vall[N],valr[N];
inline int Min(int x,int y){return x<y?x:y;}
inline int Max(int x,int y){return x>y?x:y;}
void init()
{
n=read();m=read();K=read();
if(K>14) {for(int i=1;i<=m;++i)puts("0");exit(0);}
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=m;++i)
q[i].l=read(),q[i].r=read(),q[i].bl=(q[i].l-1)/lim+1,q[i].id=i;
sort(q+1,q+m+1,cmp);
for(int i=0;i<M;++i) if(one(i)==K) kth[++cnt]=i;
memset(num,0,sizeof(num));memset(num2,0,sizeof(num2));
for(int i=1,j=n;i<=n;++i,--j)
{
for(int k=1;k<=cnt;++k) cbr[i]+=num[a[i]^kth[k]],cbl[j]+=num2[a[j]^kth[k]];
cbr[i]+=cbr[i-1];num[a[i]]++;cbl[j]+=cbl[j+1];num2[a[j]]++;
}
}
void solve()
{
q[0].l=1;q[0].r=0;
for(int i=1;i<=m;++i)
{
if(q[i].r>q[i-1].r)
{
valr[q[i-1].l-1].pb(Tnode(q[i-1].r+1,q[i].r,-1,i));
ans[i]+=cbr[q[i].r]-cbr[q[i-1].r];
}
else if(q[i].r<q[i-1].r)
{
valr[q[i-1].l-1].pb(Tnode(q[i].r+1,q[i-1].r,1,i));
ans[i]-=cbr[q[i-1].r]-cbr[q[i].r];
}
if(q[i].l<q[i-1].l)
{
vall[q[i].r+1].pb(Tnode(q[i].l,q[i-1].l-1,-1,i));
ans[i]+=cbl[q[i].l]-cbl[q[i-1].l];
}
else if(q[i].l>q[i-1].l)
{
vall[q[i].r+1].pb(Tnode(q[i-1].l,q[i].l-1,1,i));
ans[i]-=cbl[q[i-1].l]-cbl[q[i].l];
}
}
memset(num,0,sizeof(num));
for(int i=1;i<=n;++i)
{
for(int j=1;j<=cnt;++j) num[a[i]^kth[j]]++;
for(int j=0;j<(int)valr[i].size();++j)
{
int l=Max(1,valr[i][j].l),r=Min(n,valr[i][j].r),op=valr[i][j].op,id=valr[i][j].id;LL tmp=0;
for(int k=l;k<=r;++k) tmp+=num[a[k]]; ans[id]+=tmp*op;
}
}
memset(num,0,sizeof(num));
for(int i=n;i;--i)
{
for(int j=1;j<=cnt;++j) num[a[i]^kth[j]]++;
for(int j=0;j<(int)vall[i].size();++j)
{
int l=Max(1,vall[i][j].l),r=Min(n,vall[i][j].r),op=vall[i][j].op,id=vall[i][j].id;LL tmp=0;
for(int k=l;k<=r;++k) tmp+=num[a[k]]; ans[id]+=tmp*op;
}
}
for(int i=1;i<=m;++i) ans[i]+=ans[i-1];
for(int i=1;i<=m;++i) tans[q[i].id]=ans[i];
for(int i=1;i<=m;++i) write(tans[i]),putchar('\n');
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("LGP4887.in","r",stdin);
freopen("LGP4887.out","w",stdout);
#endif
init();
solve();
return 0;
}
【总结】
新科技get!