【科技】 普通莫队

考虑这样一个题:SP3267 DQUERY - D-query

一种朴素的暴力是对于每一组询问,将 \([l,r]\) 扫一遍,开桶记录答案,复杂度 \(O(nm)\)

发现其实上面的算法之所以跑得慢是因为我们重复计算的很多个数的贡献,比如第一次询问是 \([5,10]\),而第二次查询的是 \([6,11]\),其实只需要减掉 \(6\) 的贡献再加上 \(11\) 的贡献即可。

但是这个算法其实还是可以卡到 \(O(nm)\) 的,只需要这样查询:\([1,2]\)\([n-1,n]\)\([3,4]\)\([n-2,n-1] \dots\)

然后一种比较 naive 的想法就是按照某个端点排序,但是这显然不行,因为另一个端点仍然可以反复横跳。

这里就涉及了一点根号平衡的思想:我们把序列分块,假设快长为 \(b\),对于所有询问按左端点块为第一关键字排序,左端点在同一块内按右端点升序排列,那么现在复杂度来自一下几个方面:

  • 对于左端点块内是无序的,块间相对有序,每次询问最多移动 \(O(d)\) 次,所以最多移动 \(O(md)\) 次。
  • 对于右端点每个块块内以及与相邻块之间皆为 \(O(n)\) 次,所以最多移动 \(O(\frac{n^2}{d})\) 次。

假设增删均为 \(O(1)\)

于是复杂度是 \(O(md+\frac{n^2}{d})\ge O(2\sqrt{md\times \frac{n^2}{d}})=O(n\sqrt{m})\)

\(d=\frac{n}{\sqrt{m}}\) 时取得理论最优复杂度。

贴一下代码:

/*
begin time:
finish time:
author: wapmhac
*/
#include<bits/stdc++.h>
#define ll long long
#define int long long
#define lc(k) k<<1
#define rc(k) k<<1|1
#define lb lower_bound
#define orz puts("gzn ak ioi")
const int MAX=3e4+10;
const int MOD=1e9+7;
using namespace std;
inline char readchar() {
	static char buf[100000], *p1 = buf, *p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline int read() {
	int res = 0, f = 0;
	char ch = readchar();
	for(; !isdigit(ch); ch = readchar()) if(ch == '-') f = 1;
	for(; isdigit(ch);ch = readchar()) res = (res << 1) + (res << 3) + (ch ^ '0');
	return f ? -res : res;
}
inline void write(int x) {
    if(x<0){putchar('-');x=-x;}
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
int a[MAX],n,m,b,bel[MAX],ans[200010],tong[1000010],sum;
struct node{int l,r,id;}q[200010];
void add(int x){
	sum+=(++tong[a[x]]==1); 
	return ;
}
void del(int x){
	sum-=(--tong[a[x]]==0);
	return ;
}
signed main(){
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	m=read();b=ceil(n*1.0/sqrt(m)*1.0);
	for(int i=1;i<=n;i++) bel[i]=(i-1)/b+1;
	for(int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].id=i;
	sort(q+1,q+1+m,[](node x,node y){
		return (bel[x.l]==bel[y.l]?x.r<y.r:bel[x.l]<bel[y.l]);
	});
	int ql=1,qr=0;
//	for(int i=1;i<=m;i++) cout<<q[i].l<<" "<<q[i].r<<endl;
	for(int i=1;i<=m;i++){
		int l=q[i].l,r=q[i].r;
		while(ql>l) add(--ql);
		while(qr<r) add(++qr);
		while(ql<l) del(ql++);
		while(qr>r) del(qr--);
		ans[q[i].id]=sum;
	}
	for(int i=1;i<=m;i++) write(ans[i]),putchar('\n');
	return 0;
}
posted @ 2022-08-25 11:26  wapmhac  阅读(22)  评论(0编辑  收藏  举报