我可以用我基本靠蒙的数学功底来推一下式子,求一个分数,分子是sigma(1,i)C(2,i);分母是C(2,r-l+1),又因为C(2,n)=(x^2-x)/2,对于分母是可以O(1)计算的,所以我们只记录分子就可以,对于分子我们可以发现左右端点是可以递推出来的,如果a[R+1]为q则我们在由R到R+1唯一改变的值是C(2,sum[q])
C(2,sum[q])=(sum[q]^2-sum[q])/2;———-(1)
C(2,sum[q]+1)=((sum[q]+1)^2-(sum[q]+1));—–(2)
(2)式-(1)式得
(2)-(1)=2sum[q]
递推R-1,L+1,L-1的时候同理,所以我们可以用莫队算法来做这道题
然后另外一个问题是对于莫队算法分块的大小问题上一篇博客没提,莫队的分块大小其实并不是很严谨,在sqrt(N),sqrt(M),N/sqrt(M)左右都是可以的
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=50010;
typedef long long ll;
ll ans[N],cnt[N],sum,ans2[N];
int blo,a[N];
inline int F()
{
register int aa,bb;register char ch;
while (ch=getchar(),(ch<'0'||ch>'9')&&ch!='-');ch=='='?aa=bb=0:(aa=ch-'0',bb=1);
while (ch=getchar(),ch>='0'&&ch<='9')aa=(aa<<3)+(aa<<1)+ch-'0';return bb?aa:-aa;
}
struct query{
int l,r,id,lb;
bool operator <(const query &rhs)const{
return lb==rhs.lb? r<rhs.r:lb<rhs.lb;
}
}q[N];
inline ll gcd(ll a,ll b)
{
for (;a&&b;swap(a,b)){
a%=b;
}
return a|b;
}
int main()
{
// freopen("std.in","r",stdin);
memset(cnt,0,sizeof(cnt));
int n,m,l,r;
n=F(),m=F();
blo=sqrt(n);
for (int i=1;i<=n;i++)
a[i]=F();
for (int i=1;i<=m;i++)
q[i].id=i,q[i].l=F(),q[i].r=F(),q[i].lb=q[i].l/blo;
sort(q+1,q+m+1);
int nowl=1,nowr=0;
ll sum=0;
for (int i=1;i<=m;i++)
{
while (nowr<q[i].r)sum+=cnt[a[++nowr]]++;
while (nowl>q[i].l)sum+=cnt[a[--nowl]]++;
while (nowr>q[i].r)sum-=(--cnt[a[nowr--]]);
while (nowl<q[i].l)sum-=(--cnt[a[nowl++]]);
ll g=gcd(sum,1ll*(nowr-nowl+1)*(nowr-nowl)/2);
ans[q[i].id]=sum/g;
// cout<<ans[q[i].id]<<' '<<sum<<' '<<g<<' '<<nowl<<' '<<nowr<<endl;
ans2[q[i].id]=1ll*(nowr-nowl+1)*(nowr-nowl)/2/g;
}
for (int i=1;i<=m;i++)
printf("%lld/%lld\n",ans[i],ans2[i]);
return 0;
}