莫队算法,听起来就很高端的样子啊,但其实还挺好理解的。
就是如果你查询的东西满足你已知[l,r]区间的答案,就能在很快的时间内知道[l+1,r],[l-1,r],[l,r+1],[l,r-1]这四个区间的答案,就可以用莫队搞。
按照l第一关键字,r第二关键字排序,把所有的询问扫一遍,l是递增的,然后把r每次调到它该在的位置,统计答案,这样做时间复杂度上界是n^2的,有点接受无能啊……
所以就可以用我怎么想也想不到的但是神犇们就是能想到的分块了。
把区间分块,然后l在一个块内的询问按照r排序,然后每次处理一个块内的所有询问。
这样可以保证没次询问l的修改范围不超过sqrt(n),总共有m个询问,每个块内对于r的所有修改不超过n,一共有sqrt(n)块。
再加上排序的时间mlogm。
所以总的时间复杂度就是O(msqrt(n)+nsqrt(n)+mlogm)
简化一下就是O(nsqrt(n))啦!
over
代码参照了PoPo姐姐写的>_<,挺好懂的。
代码如下:
#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 50050
using namespace std;
struct query{
int l,r,num;
bool operator < (const query&) const;
}q[N];
int n,m,block;
int a[N],belong[N];
long long num[N],den[N],cnt[N];
bool query :: operator < (const query &c) const
{
return belong[l]<belong[c.l] || (belong[l]==belong[c.l] && r<c.r);
}
long long gcd(long long a,long long b)
{
if(!b) return a;
return gcd(b,a%b);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].num=i;
block=ceil(sqrt(n));
for(int i=1;i<=n;i++) belong[i]=(i-1)/block+1;
sort(q+1,q+1+m);
long long now=0;
long long l=1,r=0;
for(int i=1;i<=m;i++)
{
while(r<q[i].r) now+=cnt[a[++r]]++;
while(r>q[i].r) now-=--cnt[a[r--]];
while(l<q[i].l) now-=--cnt[a[l++]];
while(l>q[i].l) now+=cnt[a[--l]]++;
long long sum=(r-l)*(r-l+1)>>1;
long long div=gcd(sum,now);
num[q[i].num]=now/div;
den[q[i].num]=sum/div;
}
for(int i=1;i<=m;i++) printf("%lld/%lld\n",num[i],den[i]);
return 0;
}