小 Z 的袜子 题解

· · 题解

题意:有长度为 N 的序列 A,M 次询问在 [L,R] 中随机选两个不在同一位置的数值相等的概率。

解法:以下假设 $N,M$ 同阶。 显然只需要计算区间内有多少**对**相等的值。 考虑根号分治。考虑对出现次数的多少分类讨论。 - $A_i$ 出现了至少 $\sqrt N$ 次。这样的**值** $A_i$ 最多有 $\sqrt N$ 个。对于每个这样的值,维护它的前缀和数组,每次询问对于每个这样的值查询其出现次数 $c$,将 $\dfrac{c(c-1)}{2}$ 累加到答案中即可。复杂度 $O(N\sqrt N)$。 - $A_i$ 出现了不到 $\sqrt N$ 次。这样的相等**对**数 $(A_L,A_R)$ 不超过 $N\sqrt N$。我们将询问按照 $R$ 排序,遍历到 $R$ 时将所有的 $L$ 的权值加 $1$。这样,询问 $[L,R]$ 的权值就是 $[L,R]$ 内所有数权值的和。长度为 $N$ 的数组,$O(N\sqrt N)$ 次单点修改,$O(N)$ 次区间和,可以使用分块维护。复杂度 $O(N\sqrt N)$。 综上,复杂度 $O(N\sqrt N)$。 主要代码: ```cpp const int N=244; struct fenwick{ int a[50005]; int s[N]; void add(int id) { ++a[id]; ++s[(id-1)/N]; } int sum(int id) { int v=0; rep(j,id/N) { v+=s[j]; } rep(j,id%N) { v+=a[id-j]; } return v; } }; fenwick tree; int main() { cin>>n>>m; rep1(i,n) { cin>>v[i];pos[v[i]].pb(i); } rep1(i,50000) { if(pos[i].size()>=N) { fl[i]=1;special.pb(i); } else fl[i]=0; } rep(i,special.size()) sp_sum[0].pb(0); rep1(i,n) { rep(j,special.size()) { sp_sum[i].pb(sp_sum[i-1][j]+(v[i]==special[j])); } } rep1(i,m) { cin>>l[i]>>r[i];ids[r[i]].pb(i); } rep1(i,n) { if(!fl[v[i]]) { rep(j,pos[v[i]].size()) { int x=pos[v[i]][j]; if(x<i) { tree.add(x); } } } ll t=tree.sum(i); rep(j,ids[i].size()) { int x=ids[i][j]; int L=l[x]; a[x]=t-tree.sum(L-1); rep(k,special.size()) { ll t=sp_sum[i][k]-sp_sum[L-1][k]; a[x]+=t*(t-1)/2; } b[x]=(i-L+1ll)*(i-L)/2; if(b[x]==0) b[x]=1; else { ll d=mygcd(a[x],b[x]); a[x]/=d;b[x]/=d; } } } rep1(i,m) cout<<a[i]<<'/'<<b[i]<<endl; return 0; }