首先让我们考虑一下答案的表达式。
设当前的询问区间为 ,且在当前区间中第 种袜子有 支,那么
令
展开组合数:
于是我们先的任务就是求得当前区间的 即可,莫队搞一搞,注意特判与最后的分数化为最简。
参考代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define SG string
#define DB double
#define LL long long
using namespace std;
const LL Max=5e4+5;
struct Node{
LL X,Y,Id;
}Ask[Max];
LL N,M,S,Tot=1ll,A[Max],B[Max],Cnt[Max],Ans_X[Max],Ans_Y[Max];
inline LL Read(){
LL X=0;char CH=getchar();bool F=0;
while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
return F?-X:X;
}
inline void Write(LL X){
if(X<0)X=-X,putchar('-');
if(X>9)Write(X/10);
putchar(X%10+48);
}
bool Cmp(Node P,Node Q){
return B[P.X]==B[Q.X]?P.Y<Q.Y:B[P.X]<B[Q.X];
}
LL GCD(LL X,LL Y){
while(Y^=X^=Y^=X%=Y);
return X;
}
void Insert(LL X){
Tot-=Cnt[X]*Cnt[X];++Cnt[X];Tot+=Cnt[X]*Cnt[X];
}
void Delete(LL X){
Tot-=Cnt[X]*Cnt[X];--Cnt[X];Tot+=Cnt[X]*Cnt[X];
}
int main(){
LL I,J,K;
N=Read(),M=Read();S=sqrt(N);
for(I=1;I<=N;I++){
A[I]=Read();
B[I]=I/S;
}
for(I=1;I<=M;I++){
Ask[I].Id=I;
Ask[I].X=Read();
Ask[I].Y=Read();
}
sort(Ask+1,Ask+1+M,Cmp);
LL Left=1ll,Right=1ll;++Cnt[A[1]];
for(I=1;I<=M;I++){
if(Ask[I].X==Ask[I].Y){
Ans_X[Ask[I].Id]=0;
Ans_Y[Ask[I].Id]=1;continue;
}
while(Right<Ask[I].Y){
Insert(A[++Right]);
}
while(Right>Ask[I].Y){
Delete(A[Right--]);
}
while(Left<Ask[I].X){
Delete(A[Left++]);
}
while(Left>Ask[I].X){
Insert(A[--Left]);
}
LL Length=Ask[I].Y+1-Ask[I].X;
Ans_X[Ask[I].Id]=Tot-Length;
Ans_Y[Ask[I].Id]=Length*(Length-1);
LL _GCD=GCD(Ans_X[Ask[I].Id],Ans_Y[Ask[I].Id]);
Ans_X[Ask[I].Id]/=_GCD;
Ans_Y[Ask[I].Id]/=_GCD;
}
for(I=1;I<=M;I++){
Write(Ans_X[I]),putchar('/'),Write(Ans_Y[I]),putchar('\n');
}
return 0;
}