[国家集训队]小Z的袜子
题目大意
莫队入门题
序列中有\(n\)个数
\(m\)个询问,每次询问在区间\([l,r]\)中,随机取两个数,数值相同的概率
\(1 \leq n,m \leq 50000\)
解题思路
既然是莫队入门题,当然要用莫队做
常规操作:对询问排序!
排序就不多说,重点在指针移动时对答案的贡献
首先,令\(cnt[i]\)表示区间\([l,r]\)中数值为\(i\)的个数
那么我们很容易推出公式:
\[ans=\frac{\sum_{i=0}^{n}C(cnt_i,2)}{C(r-l+1,2)}
\]
设增加一个颜色\(P\)(右指针右移一格或左指针左移一格),
那么分子变化量
\[\begin{align}
\Delta &=C(cnt_P+1,2)-C(cnt_P,2) \\
&=cnt_P
\end{align}\]
分母变化量
\[\begin{align}
\Delta &=C(r-l+2,2)-C(r-l+1,2) \\
&=r-l+1
\end{align}\]
于是指针移动时可以\(O(1)\)转移分子和分母
#include<iostream>
#include<cstdio>
#include<algorithm>
int n,m;
int read(){
char ch;while (isspace(ch=getchar()));
int ret=ch&15;
while (isdigit(ch=getchar())) ret=(ret<<1)+(ret<<3)+(ch&15);
return ret;
}
struct node{
int col,inK;
}T[100000];
struct query{
int l,r;
int id;
bool operator < (const query &P) const {return T[l].inK<T[P.l].inK||T[l].inK==T[P.l].inK&&r<P.r;}
}Q[100000];
int sqrt(long long k){
long long l=0,r=k,mdl;
while (l<r){
mdl=(l+r+1)>>1;
if (mdl*mdl<=k) l=mdl;
else r=mdl-1;
}
return l;
}
long long gcd(long long a,long long b){return (!b)?a:(gcd(b,a%b));}
int blo;
int cnt[100000],x,y,l=1,r=0;
std::pair<int,int> ans[100000];
int main(){
n=read();m=read();
for (int i=1;i<=n;i++) T[i].col=read();
blo=sqrt(n);
for (int i=1;i<=blo;i++){
int L=n*(i-1)/blo+1,R=n*i/blo;
for (int j=L;j<=R;j++) T[j].inK=i;
}
for (int i=1;i<=m;i++){
Q[i].l=read(),Q[i].r=read();
Q[i].id=i;
}
std::sort(Q+1,Q+m+1);
for (int i=1;i<=m;i++){
while (l<Q[i].l){cnt[T[l].col]--;x-=cnt[T[l].col];y-=(r-l);l++;}
while (r>Q[i].r){cnt[T[r].col]--;x-=cnt[T[r].col];y-=(r-l);r--;}
while (l>Q[i].l){l--;x+=cnt[T[l].col];y+=r-l;cnt[T[l].col]++;}
while (r<Q[i].r){r++;x+=cnt[T[r].col];y+=r-l;cnt[T[r].col]++;}
if (l==r) ans[Q[i].id]=std::make_pair(0,1);
else{
int d=gcd(x,y);
ans[Q[i].id]=std::make_pair(x/d,y/d);
}
}
for (int i=1;i<=m;i++) printf("%d/%d\n",ans[i].first,ans[i].second);
}