题意
有一个长度为 \(n\) 的序列 \(\{c_i\}\) 。现在给出 \(m\) 个询问,每次给出两个数 \(l,r\) ,从编号在 \(l\) 到 \(r\) 之间的数中随机选出两个不同的数,求两个数相等的概率。
思路
莫队算法模板题
对于区间 \([l,r]\),以 \(l\) 所在块的编号为第一关键字, \(r\) 为第二关键字从小到大排序。
然后从序列的第一个询问开始计算答案,第一个询问通过直接暴力算出,复杂度为 \(O(n\sqrt{n})\) ,后面的询问在前一个询问的基础上得到答案。
#include <cmath>
#include <cstdio>
#include <algorithm>
#define ll long long
#define bi(a) ((a - 1) / sqn + 1)
typedef struct {
ll l, r, i;
} Node;
const int maxn = 5e4 + 10;
Node q[maxn];
ll n, m, sqn, arr[maxn], l, r, ans, col[maxn], sub[maxn], mot[maxn];
bool cmp(Node a, Node b) {
if (bi(a.l) == bi(b.l)) return a.r < b.r;
return a.l < b.l;
}
ll gcd(ll a, ll b) { return !b ? a : gcd(b, a % b); }
int main() {
scanf("%lld %lld", &n, &m); sqn = std::sqrt(n);
for (int i = 1; i <= n; ++ i) scanf("%lld", arr + i);
for (int i = 1; i <= m; ++ i) {
scanf("%lld %lld", &q[i].l, &q[i].r); q[i].i = i;
}
std::sort(q + 1, q + m + 1, cmp); l = r = q[1].l, col[arr[l]] ++; // obviously
for (int i = 1, gcdnum; i <= m; ++ i) {
for (; l < q[i].l; l++) col[arr[l]]--, ans -= col[arr[l]];
for (--l; l >= q[i].l; l--) ans += col[arr[l]], col[arr[l]]++;
for (; r > q[i].r; r--) col[arr[r]]--, ans -= col[arr[r]];
for (++r; r <= q[i].r; r++) ans += col[arr[r]], col[arr[r]]++;
sub[q[i].i] = ans, l = q[i].l, r = q[i].r;
mot[q[i].i] = 1ll * ((r - l) * (r - l + 1)) >> 1;
}
for (int i = 1; i <= m; ++ i) {
ll gcdn = gcd(sub[i], mot[i]);
printf("%lld/%lld\n", sub[i] / gcdn, mot[i] / gcdn);
}
}