ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

莫队二次离线

2022-02-17 08:32:32  阅读:202  来源: 互联网

标签:二次 int 离线 ask 莫队 预处理 指针


当挪动一次莫队指针的复杂度为 \(O(k)\) 时,普通莫队的复杂度为 \(O(n\sqrt(n)k)\)
设 \(f(x,l,r)\) 为 \(x\) 对 \([l,r]\) 区间的贡献,那么当:

  • \(f(x,l,r)\) 只与 \([l,r]\) 内元素有关
  • \(f(x,l,r)=f(x,1,r)-f(x,1,l-1)\)

时,可以用莫队二次离线优化到 \(O(nk+n\sqrt(n))\)

以目前的 \(r\) 指针小于当前询问的右端点(设当前询问的左右端点分别是 \(L,R\)),指针向右移动为例,对答案的贡献就是

\[\sum_{x=r+1}^R f(x,L,x-1)=\sum_{x=r+1}^R f(x,1,x-1)-\sum_{x=r+1}^R f(x,1,L-1) \]

这个 \(f(x,1,x-1)\) 可以 \(O(nk)\) 预处理出来,而后面那一项用 \((L-1,r+1,R,i,k)\) 表示,进行第二次离线。其中 \(i\) 是询问的编号,\(k\) 是正一或负一的系数
然后莫队结束后对 \(L-1\) 从小到大处理

移动其他指针类似,会产生一个 \(f(x,1,x)\),同样预处理即可,但一般来说一个数不会对自己产生贡献,所以只预处理一个就好了
当在第 \(i\) 个询问移动莫队指针时,产生的贡献会被算到 \([i,m]\) 中的所有询问里,所以这样求得的答案实际上是一个差分后的答案

模板题:https://www.luogu.com.cn/problem/P4887
预处理时,由于 \(a\operatorname{xor} b=c\Rightarrow a=b\operatorname{xor} c\),记录下所有 \(\operatorname{popcount}\) 为 \(k\) 的数,开桶即可

#define N 100006
#define B 316
int n,m,k;
struct Ask{
	int l,r,id;
};
Ask ask[N];
struct Node{
	int L,R,i,k;
};
std::vector<Node>change[N];
int a[N],belong[N];
int fxx[N];
long long ans[N];
int main(){
	n=read();m=read();k=read();
	if(k>14){
		for(int i=1;i<=m;i++) write("0\n");
		return RET;
	}
	for(int i=1;i<=n;i++) a[i]=read(),belong[i]=(i-1)/B+1;
	static int num[16384],t[16384];
	for(int i=0;i<16384;i++)if(__builtin_popcount(i)==k) num[++num[0]]=i;
	for(int i=1;i<=n;i++){
		fxx[i]=t[a[i]];
		for(int j=1;j<=num[0];j++) t[a[i]^num[j]]++;
	}
	for(int i=1;i<=m;i++) ask[i].l=read(),ask[i].r=read(),ask[i].id=i;
	std::sort(ask+1,ask+1+m,[](const Ask &a,const Ask &b){return belong[a.l]==belong[b.l]?a.r<b.r:a.l<b.l;});
	for(int l=1,r=0,i=1;i<=m;i++){
		if(r<ask[i].r) change[l-1].push_back({r+1,ask[i].r,i,-1});
		while(r<ask[i].r) ans[ask[i].id]+=fxx[++r];
		if(l>ask[i].l) change[r].push_back({ask[i].l,l-1,i,1});
		while(l>ask[i].l) ans[ask[i].id]-=fxx[--l];
		if(r>ask[i].r) change[l-1].push_back({ask[i].r+1,r,i,1});
		while(r>ask[i].r) ans[ask[i].id]-=fxx[r--];
		if(l<ask[i].l) change[r].push_back({l,ask[i].l-1,i,-1});
		while(l<ask[i].l) ans[ask[i].id]+=fxx[l++];
	}
	std::memset(t,0,sizeof t);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=num[0];j++) t[a[i]^num[j]]++;
		for(const Node &x:change[i]){
			for(int j=x.L;j<=x.R;j++){
				if(j<=i&&!k) ans[ask[x.i].id]+=x.k*(t[a[j]]-1);
				else ans[ask[x.i].id]+=x.k*t[a[j]];
			}
		}
	}
	for(int i=1;i<=m;i++) ans[ask[i].id]+=ans[ask[i-1].id];
	for(int i=1;i<=n;i++) writeEN(ans[i]);
	return RET;
}

标签:二次,int,离线,ask,莫队,预处理,指针
来源: https://www.cnblogs.com/suxxsfe/p/15903044.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有