传送门
首先求概率的方法为:假设在LR这段区间里颜色为 x,y,z… 的袜子分别有 a,b,c… 个。
那么概率(利用排列组合推导)
p=a(a−1)2+b(b−1)2+c(c−1)2+…(R−L+1)(R−L)2
=a2+b2+c2+…−(a+b+c+…)(R−L+1)(R−L)
a2+b2+c2+…−(R−L+1)(R−L+1)(R−L)
那么我们用莫队算法+分块乱搞就能求出答案。 O(nn√)
首先离线操作将区间排序,按照左端点的分块的编号,同一块内按照右端点排序。然后暴力更改区间求值即可。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define LL long long
const int max_n=5e4+5;
const int max_m=5e4+5;
const int max_c=5e4+5;
struct hp{
LL l,r,block,num;
}f[max_m];
struct hq{ LL up,down; }ans[max_m];
LL a[max_n],c[max_c];
LL n,nn,m,L,R,up,down,GCD,upans,downans;
inline int cmp(hp a,hp b){
return a.block<b.block||(a.block==b.block&&a.r<b.r)||(a.block==b.block&&a.r==b.r&&a.l<b.l);
}
inline LL gcd(LL a,LL b){
if (!b) return a;
else return gcd(b,a%b);
}
int main(){
scanf("%lld%lld",&n,&m);
nn=sqrt(n);
for (int i=1;i<=n;++i)
scanf("%lld",&a[i]);
for (int i=1;i<=m;++i){
scanf("%lld%lld",&f[i].l,&f[i].r);
f[i].num=i;
}
for (int i=1;i<=m;++i){
if (f[i].l%nn==0)
f[i].block=f[i].l/nn;
else f[i].block=f[i].l/nn+1;
}
sort(f+1,f+m+1,cmp);
L=R=1;
c[a[1]]=1;
up=0;
for (int i=1;i<=m;++i){
up+=R-L+1;
if (f[i].l<L)
for (int j=L-1;j>=f[i].l;--j){
up-=c[a[j]]*c[a[j]];
up+=(++c[a[j]])*c[a[j]];
}
if (f[i].r>R)
for (int j=R+1;j<=f[i].r;++j){
up-=c[a[j]]*c[a[j]];
up+=(++c[a[j]])*c[a[j]];
}
if (f[i].l>L)
for (int j=L;j<f[i].l;++j){
up-=c[a[j]]*c[a[j]];
up+=(--c[a[j]])*c[a[j]];
}
if (f[i].r<R)
for (int j=R;j>f[i].r;--j){
up-=c[a[j]]*c[a[j]];
up+=(--c[a[j]])*c[a[j]];
}
L=f[i].l,R=f[i].r;
up-=R-L+1;
down=(R-L+1)*(R-L);
if (!up){
ans[f[i].num].up=0;
ans[f[i].num].down=1;
continue;
}
GCD=gcd(up,down);
upans=up/GCD;
downans=down/GCD;
ans[f[i].num].up=upans;
ans[f[i].num].down=downans;
}
for (int i=1;i<=m;++i)
printf("%lld/%lld\n",ans[i].up,ans[i].down);
}