强有力的暴力算法,重点是通过排序来使暴力变快。
主要解决区间内计数问题:
例题:小B的询问
用这道题主要是发现它和我做的一道二维莫队一模一样
n个数,m个询问
我们先写一个暴力,和平常的不一样:
一个左边界,一个右边界,记录每一个数在这个区间里的出现次数和答案,
对于每一个询问,移动到对应区间就可以拿到答案。
这么做是O(nm),超时。
关键
这个时候就用到莫队精髓了--排序。
现将每一个坐标分成从小到大b组,
现将左下标按组号排序,组内
偶数组:右下标从小到大。
奇数组:右下标从大到小。
这样排可以得到
对于同一组内的移动:
左下标每次最多移动b次,右下标总共最多移动n次。
换块时:
左下标每次最多移动2*b次,右下标移动最多n次。
In total:
左下标最多移动了q*b次,右下标移动n*(n/b)次(n/b为换块次数)
O(q*b+n*n/b)
将b取
O(q*sqrt(n)+n*sqrt(n))
那前面为什么奇偶排序呢?
这样就可以在r返回的时候做一组
优先级优化:
这个就是++,--什么的,会快很多
代码:
#include<bits/stdc++.h>
using namespace std;
#define Mod (998244353)
#define LL long long
#define M (50005)
#define isdigit(x) ((x) >= '0' && (x) <= '9')
#pragma GCC optimize(3)
LL read() {
LL res = 0;
char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) res = (res << 1) + (res << 3) + c - 48, c = getchar();
return res;
}
void write(LL x) {
if(x / 10) write(x / 10);
putchar(x % 10 + '0');
}
LL belong[M];
struct node{
LL l,r,id;
bool operator <(node b)const{
return (belong[l] ^ belong[b.l]) ? belong[l] < belong[b.l] : ((belong[l] & 1) ? r < b.r : r > b.r);
}
}q[M];
LL n,m,num[M],cnt[M],ans[M],kk;
int main(){
n=read();
m=read();
kk=read();
LL tp=sqrt(n);
LL nn=ceil((double)n/tp);
for(int i=1;i<=nn;i++)
for(int j=(i-1)*tp+1;j<=i*tp;j++)
belong[j]=i;
for(int i=1;i<=n;i++)
num[i]=read();
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);
LL s=0;
LL l=1,r=0;
for(int i=1;i<=m;i++){
LL ql=q[i].l,qr=q[i].r;
while(l<ql) s-=cnt[num[l]]+(--cnt[num[l++]]);
while(r>qr) s-=cnt[num[r]]+(--cnt[num[r--]]);
while(l>ql) s+=(cnt[num[--l]]++)+cnt[num[l]];
while(r<qr) s+=(cnt[num[++r]]++)+cnt[num[r]];
ans[q[i].id]=s;
}
for(int i=1;i<=m;i++)
printf("%lld\n",ans[i]);
}