题目链接:小Z的袜子

题意:$n$只袜子,$m$个询问,每次回答有多大概率在$[L,R]$区间内抽到两只颜色相同的袜子

思路:普通莫队,如果两个询问左端点在一个块内,则按询问右端点排序,否则按照所在块的序号排序,维护一个数组$num$,表示在区间$[L,R]$中,颜色为$c$的袜子有$num[c]$只,令变量$res=\sum_{c}num[c]*(num[c]-1)/2$,显然对于每个区间$[L,R]$抽到同色袜子的概率就是$\frac{res}{C_{R-L+1}^{2}}$,每次移动区间时修改$num[c]$,同时更新$res$即可,时间复杂度$O(n^{\frac{3}{2}})$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>

using namespace std;

typedef long long ll;

const int N = 50010;

struct node {
int l, r, id;
};

int n, m, a[N];
int block, belong[N], tot, l[N], r[N];
ll num[N], res, mol[N], den[N];
node q[N];

void build()
{
block = sqrt(n);
tot = n / block;
if (n % block) tot++;
for (int i = 1; i <= tot; i++) {
l[i] = (i - 1) * block + 1;
r[i] = i * block;
}
r[tot] = n;
for (int i = 1; i <= n; i++)
belong[i] = (i - 1) / block + 1;
}

bool cmp(node a, node b)
{
if (belong[a.l] != belong[b.l])
return belong[a.l] < belong[b.l];
return a.r < b.r;
}

void add(int x)
{
res = res - num[a[x]] * (num[a[x]] - 1) / 2;
num[a[x]]++;
res = res + num[a[x]] * (num[a[x]] - 1) / 2;
}

void sub(int x)
{
res = res - num[a[x]] * (num[a[x]] - 1) / 2;
num[a[x]]--;
res = res + num[a[x]] * (num[a[x]] - 1) / 2;
}

ll c(int n)
{
return (ll)n * (n - 1) / 2;
}

ll gcd(ll a, ll b)
{
return 0 == b ? a : gcd(b, a % b);
}

int main()
{
scanf("%d%d", &n, &m);
build();
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= m; i++) {
scanf("%d%d", &q[i].l, &q[i].r);
den[i] = c(q[i].r - q[i].l + 1);
q[i].id = i;
}
sort(q + 1, q + m + 1, cmp);
int L = 1, R = 0;
for (int i = 1; i <= m; i++) {
while (q[i].l < L) add(--L);
while (q[i].r > R) add(++R);
while (q[i].l > L) sub(L++);
while (q[i].r < R) sub(R--);
mol[q[i].id] = res;
}
for (int i = 1; i <= m; i++) {
ll d = gcd(mol[i], den[i]);
if (0 == mol[i]) printf("0/1\n");
else printf("%lld/%lld\n", mol[i] / d, den[i] / d);
}
return 0;
}