#莫队,分块#bzoj 3781 洛谷 2709 小B的询问

本文详细介绍了一种高效的区间查询算法——莫队算法,并通过一个具体的实战案例来展示其应用。通过对暴力解法进行优化,利用区间排序和分块的思想,实现了一个时间复杂度为O(n√n)的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

一共有M个询问,每个询问给定一个区间[L..R][L..R][L..R],求Σ(c(i)2)\Sigma(c(i)^2)Σ(c(i)2)的值,其中c(i)c(i)c(i)表示数字iii[L..R][L..R][L..R]中的重复次数。


分析

然而,这道题看起来是暴力,实际上,还是暴力,只是用一种特殊的方式暴力,就是莫队,莫队是用一种O(1)O(1)O(1)的方式移动一个区间,然而这样看起来像O(n2)O(n^2)O(n2),所以再找一种好的办法,排序!当然除此之外,每个区间不超过n\sqrt nn,那么这种分块的东西,时间复杂度O(nn)O(n\sqrt n)O(nn)


代码

#include <cstdio>
#include <cmath>
#include <algorithm>
struct rec{int l,r,rk;}a[50001];
int n,m,b[50001],ans[50001],cnt[50001],t;
int in(){
	int ans=0; char c=getchar();
	while (c<48||c>57) c=getchar();
	while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
	return ans;
}
bool cmp(const rec &x,const rec &y){
	if ((x.l-1)/t!=(y.l-1)/t) return (x.l-1)/t<(y.l-1)/t;
	    else return x.r<y.r;
}
void print(int ans){if (ans>9) print(ans/10); putchar(ans%10+48);}
int main(){
    n=in(); m=in(); in(); t=sqrt(n);
    for (int i=1;i<=n;i++) b[i]=in();
    for (int i=1;i<=m;i++)
    	a[i].l=in(),a[i].r=in(),a[i].rk=i;
    std::stable_sort(a+1,a+1+m,cmp);//按照区间排序
    int l=1,r=0,Ans=0;
    for (int i=1;i<=m;i++){
    	while (l>a[i].l) l--,cnt[b[l]]++,Ans+=cnt[b[l]]-1<<1|1;//往左边移
    	while (r<a[i].r) r++,cnt[b[r]]++,Ans+=cnt[b[r]]-1<<1|1;//往右边移
    	while (l<a[i].l) cnt[b[l]]--,Ans-=cnt[b[l]]<<1|1,l++;//往回移
    	while (r>a[i].r) cnt[b[r]]--,Ans-=cnt[b[r]]<<1|1,r--;//往回移
    	ans[a[i].rk]=Ans;
    }
    for (int i=1;i<=m;i++) print(ans[i]),putchar('\n');
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值