P2709 莫队

小B的询问

题目描述

小B 有一个长为 \(n\) 的整数序列 \(a\),值域为 \([1,k]\)
他一共有 \(m\) 个询问,每个询问给定一个区间 \([l,r]\),求:

\[\sum\limits_{i=1}^k c_i^2 \]

其中 \(c_i\) 表示数字 \(i\)\([l,r]\) 中的出现次数。
小B请你帮助他回答询问。

输入格式

第一行三个整数 \(n,m,k\)

第二行 \(n\) 个整数,表示 小B 的序列。

接下来的 \(m\) 行,每行两个整数 \(l,r\)

输出格式

输出 \(m\) 行,每行一个整数,对应一个询问的答案。

样例 #1

样例输入 #1

6 4 3
1 3 2 1 1 3
1 4
2 6
3 5
5 6

样例输出 #1

6
9
5
2

提示

【数据范围】
对于 \(100\%\) 的数据,\(1\le n,m,k \le 5\times 10^4\)

莫队

将查询区间离线 首先按照区间左端点所属的块排序

若左端点所属块相同 那么按右端点排序

奇偶性优化 loc[x.l]&1 则 return x.r>y.r else 则 return x.r<y.r

然后双指针遍历统计即可 注意

我们Add的时候当前点是处理过的(已经Add了)所以先L-- R++ 再Add

我们Delete的时候当前点还没有删 所以先Delete 再 L++ R--

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5;
int n,m,k;
int len,loc[N],a[N],cnt[N],Ans=0,tot,ans[N];
struct md { //莫队
	int l,r,id;
} q[N];
inline bool cmp(md x,md y) {
	if(loc[x.l]==loc[y.l]) {
		if(loc[x.l]&1)return x.r>y.r;
		return x.r<y.r;
	}
	return loc[x.l]<loc[y.l];
}
inline void Add(int x) {
	cnt[x]++;
//	if(cnt[x]==1)Ans++;
	Ans+=(cnt[x]<<1)-1;
}
inline void Delete(int x) {
	cnt[x]--;
//	if(cnt[x]==0)Ans--;
	Ans-=(cnt[x]<<1|1);
}
signed main() {
	ios::sync_with_stdio(false);
	cin>>n>>m>>k;
	len=sqrt(n);
	tot=n/len;
	tot+=(n%len!=0);
	for(int i=1; i<=n; i++)loc[i]=(i-1)/len+1;
	for(int i=1; i<=n; i++)cin>>a[i];
	int l,r;
	for(int i=1; i<=m; i++)
		cin>>l>>r,q[i].l=l,q[i].r=r,q[i].id=i;
	sort(q+1,q+m+1,cmp);
	int L=1,R=0;
	for(int i=1; i<=m; i++) {
		while(L>q[i].l)L--,Add(a[L]);
		while(R<q[i].r)R++,Add(a[R]);
		while(L<q[i].l)Delete(a[L]),L++;
		while(R>q[i].r)Delete(a[R]),R--;
		ans[q[i].id]=Ans;
	}
	for(int i=1; i<=m; i++)cout<<ans[i]<<"\n";
	return 0;
}
posted @ 2023-04-18 20:06  N0zoM1z0  阅读(11)  评论(0编辑  收藏  举报