传送门
莫队入门题.
作为众多莫队难题里的一股清流.这题能很好的体现莫队的优雅.
莫队的复杂度证明过程和模板网上很多就不赘述了,说说add和del需要注意的地方.根据组合数学的知识我们可以知道,假设区间长度为len.那么拿到两个相同颜色的袜子的概率为:a1×(a1-1)/len×(len-1)+a2…+an…先考虑add.(a+1)*a展开就是a2+a.比原先a2-a加了2a那么多,del即是变成了a2-a-2a+2.比原先多了-2a+2.两个操作就结束了.
然后还有一点处理之后的询问顺序已经乱了,在处理区间长度为1的特殊情况时不能用当前询问的左右端点,直接用记录好的长度就行了.
#pragma GCC optimize(2)
#define LL long long
#define pll pair<LL,LL>
#define pq priority_queue
#define ULL unsigned long long
#define pb push_back
#define mem(a,x) memset(a,x,sizeof a)
#define pii pair<int,int>
#define fir(i,a,b) for(int i=a;i<=b;++i)
#define afir(i,a,b) for(int i=a;i>=b;--i)
#define ft first
#define vi vector<int>
#define sd second
#define ALL(a) a.begin(),a.end()
#define bug puts("-------")
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
inline int read(){
int x = 0,f=1;char ch = getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct Q{
int l,r,p;
}q[N];
int num,blo,n,m,L[N],R[N],pos[N];
LL a[N],cnt[N];
pll ans[N];
LL gcd(LL a,LL b){
return b?gcd(b,a%b):a;
}
bool cmp(Q a,Q b){
return pos[a.l]^pos[b.l]?pos[a.l] < pos[b.l]:(pos[a.l]&1)?a.r < b.r:a.r > b.r;
}
void build(){
blo = sqrt(n);
num = n/blo;
fir(i,1,num){
L[i] = (i-1)*blo+1;
R[i] = i*blo;
}
if(n % blo){
num++;
L[num] = R[num-1]+1;
R[num] = n;
}
fir(i,1,num){
fir(j,L[i],R[i]){
pos[j] = i;
}
}
}
LL cur = 0;
void add(int x){
cur += 2*cnt[a[x]];
cnt[a[x]]++;
}
void del(int x){
cur += -2*cnt[a[x]] + 2;
cnt[a[x]]--;
}
int main(){
n = read();m = read();
fir(i,1,n) a[i] = read();
fir(i,1,m){
q[i].l = read();
q[i].r = read();
q[i].p = i;
}
build();
sort(q+1,q+1+m,cmp);
int l=1,r=0;
fir(i,1,m){
while(q[i].l < l) add(--l);
while(q[i].l > l) del(l++);
while(q[i].r > r) add(++r);
while(q[i].r < r) del(r--);
ans[q[i].p] = make_pair(cur,q[i].r-q[i].l+1);
}
fir(i,1,m){
if(ans[i].sd == 1 || !ans[i].ft) printf("0/1\n");
else{
LL g = gcd(ans[i].ft,ans[i].sd*(ans[i].sd-1));
printf("%lld/%lld\n",ans[i].ft/g,1LL*ans[i].sd*(ans[i].sd-1)/g);
}
}
return 0;
}