[题解]luogu-P1494 小Z的袜子 普通莫队
此题是普通莫队第一题,考虑每个询问,我们排序后,用一个数组\(s\)记录每个数在当前区间出现次数。
不难得出,第\(num\)个袜子对第\(i\)个询问的贡献即为每次从s[color[num]]个袜子里选出两个袜子的概率,即
\[\frac{
\begin{pmatrix}
2 \\
s[color[num]] \\
\end{pmatrix}}{
\begin{pmatrix}
2 \\
(q[i].r - q[i].l + 1) \\
\end{pmatrix}
} \]
可列开为
\[\frac{s[color[num]]*(s[color[num]] - 1) / 2}{(q[i].r - q[i].l + 1) * (q[i].r - q[i].l) / 2}
\]
化简得
\[\frac{s[color[num]]*(s[color[num]] - 1)}{(q[i].r - q[i].l + 1) * (q[i].r - q[i].l)}
\]
每个问题的区间我们可以另开一个数组记录,在最后输出答案的时候输出就可以了
所以我们只用维护一个\(s\)数组,代码如下
#include <cstdio>
#include <algorithm>
#include <cmath>
typedef long long LL;
const int N = 50030;
int n, m, blo, L, R;
int c[N], id[N], s[N];
LL sum, gcd;
LL Ans[N], l[N], r[N];
struct question
{
int l, r, num;
} q[N];
int cmp(question x, question y)
{
if (id[x.l] != id[y.l]) return id[x.l] < id[y.l];
if (id[x.l] % 2 == 1) return x.r < y.r;
else return x.r > y.r;
}
LL GCD(LL a, LL b)
{
if (b == 0) return a;
else return GCD(b, a % b);
}
int main()
{
scanf ("%d%d", &n, &m);
blo = sqrt(n);
for (int i = 1; i <= n; ++i)
{
scanf ("%d", &c[i]);
id[i] = (i - 1) / blo + 1;
}
for (int i = 1; i <= m; ++i)
{
scanf ("%lld%lld", &l[i], &r[i]);
q[i].l = l[i];
q[i].r = r[i];
q[i].num = i;
}
std::sort(q + 1, q + m + 1, cmp);
L = 1;
for (int i = 1; i <= m; ++i)
{
while (L > q[i].l) {--L; sum -= s[c[L]] * (s[c[L]] - 1) / 2; s[c[L]]++; sum += s[c[L]] * (s[c[L]] - 1) / 2;}
while (R < q[i].r) {++R; sum -= s[c[R]] * (s[c[R]] - 1) / 2; s[c[R]]++; sum += s[c[R]] * (s[c[R]] - 1) / 2;}
while (L < q[i].l) {sum -= s[c[L]] * (s[c[L]] - 1) / 2; --s[c[L]]; sum += s[c[L]] * (s[c[L]] - 1) / 2; ++L;}
while (R > q[i].r) {sum -= s[c[R]] * (s[c[R]] - 1) / 2; --s[c[R]]; sum += s[c[R]] * (s[c[R]] - 1) / 2; --R;}
Ans[q[i].num] = sum;//扩张在前,缩小在后
}
for (int i = 1; i <= m; ++i)
{
gcd = GCD(Ans[i], (r[i] - l[i]) * (r[i] - l[i] + 1) / 2);
if (l[i] == r[i]) printf ("0/1\n");
else printf ("%lld/%lld\n", Ans[i] / gcd, ((r[i] - l[i]) * (r[i] - l[i] + 1) / 2) / gcd);
}
return 0;
}``