小Z的袜子(hose)
题意:有一个含 n 个数,下标从1 到 n 的序列,有m次询问,每次询问一个区间,问从区间里任意取两个数,两个数相同的概率是多少
思路:区间查询,可以离线,所以也是莫队可以解决的
先考虑如何求值的问题,对于一个区间,进来一个数 x,如果前面已经有k个x存在了,那么新进的这个x可以与前面的k个x组合成k个对子,所以Ans += flag[x] 就可以统计出相同的对数了,然后区间里有 n个数的话就有 n * (n - 1) / 2 种取法。依然是进行 sqrt(n) 的分块,排序。然后每次调整完毕后求gcd,约分,存答案。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define maxn 50005
#define ll long long
struct node{
int l,r,id;
}q[maxn];
int a[maxn],pos[maxn];
ll ans[maxn][2],flag[maxn];
ll Ans = 0;
int n,m;
bool cmp(node a,node b){
if(pos[a.l] != pos[b.l])
pos[a.l] < pos[b.l];
return a.r < b.r;
}
void add(int x){
Ans += flag[a[x]];
flag[a[x]]++;
}
void dele(int x){
flag[a[x]]--;
Ans -= flag[a[x]];
}
ll Kgcd(ll a, ll b) { // 要大的在前,小的在后
if (a < b) { //若小的在前,则交换顺序
return Kgcd(b, a);
}
if (b == 0) { // 出现0 的时候,另一个就是gcd
return a;
}
if (a % 2 == 0) { //大的那个数为偶数则模2
if (b % 2 == 0) { //小的也是偶数的话也模2 直到都为奇数
return Kgcd(a >> 1, b >> 1) << 1;
} else {
return Kgcd(a >> 1, b);
}
} else {
if (b % 2 == 0) {
return Kgcd(a, b >> 1);
} else { //都为奇数则用大的减小的再继续进行
return Kgcd(b, a - b);
}
}
}
int main(){
scanf("%d %d",&n,&m);
int s = sqrt(n);
memset(flag,0,sizeof(flag));
Ans = 0;
for(int i = 1;i <= n;i++){
scanf("%d",&a[i]);
pos[i] = i / s;
}
for(int i = 1;i <= m;i++){
scanf("%d %d",&q[i].l,&q[i].r);
q[i].id = i;
}
sort(q + 1,q + 1 + m,cmp);
int l = 1,r = 0;
for(int i = 1;i <= m;i++){
while(q[i].l < l){
add(--l);
}
while(q[i].l > l){
dele(l++);
}
while(q[i].r < r){
dele(r--);
}
while(q[i].r > r){
add(++r);
}
ll x = (q[i].r - q[i].l + 1);
ll tmp = x * (x - 1) / 2;
ll g = Kgcd(tmp,Ans);
ans[q[i].id][0] = Ans / g;
ans[q[i].id][1] = tmp / g;
}
for(int i = 1;i <= m;i++){
printf("%lld/%lld\n",ans[i][0],ans[i][1]);
}
return 0;
}