描述
- 每个询问在一行中输出分数A/B表示从该询问的区间[L,R]中随机抽出两只袜子颜色相同的概率。
分析
- 区间无修改的题目, 只需要求出各种颜色的数量即可, 所以可以用莫队.
- 如果一种颜色 i 在区间 [L, R] 内的数目是 c[i], 那么随机抽出两只袜子颜色相同的概率等于 ΣC(c[i], 2) / C(R-L+1, 2).
- 发现组合数的 m 位置都是2, 所以直接展开来算, 得到 Σc[i]*(c[i]-1) / [(R-L+1)*(R-L)].
- 这样分母我们已知, 分子可以通过状态转移得到, 每次如果新加入一个结点, 颜色为 a, 则先减掉原来 a 对分子的贡献即 c[i]*(c[i]-1), 然后++c[i], 再让分子加上现在 a颜色对分子的贡献, 即 c[i]*(c[i]-1). 删掉一个结点类似. 所以就用 O(1) 的时间从 [L, R] 转移到了 [L-1, R] 或者 [L+1, R] 或者 [L, R-1] 或者 [L, R+1].
- 再计算分子分母的gcd即可
- 不要忘记开 long long.
#include
#include
#include
using namespace std;
typedef long long int lli;
const int maxn = 50000 + 10;
int size;
int A[maxn];
int c[maxn];
lli X[maxn], Y[maxn];
struct Query {
int L, R, id;
bool operator < (const Query& rhs) const {
if(L/size != rhs.L/size) return L < rhs.L;
return R < rhs.R;
}
} Q[maxn];
#define q Q[i]
lli gcd(lli a, lli b) {
return b == 0 ? a : gcd(b, a % b);
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++)
scanf("%d", &A[i]);
for(int i = 1; i <= m; i++)
scanf("%d %d", &q.L, &q.R), q.id = i;
size = sqrt(n);
sort(Q+1, Q+m+1);
int L = 1, R = 0;
lli x = 0;
for(int i = 1; i <= m; i++) {
while(L < q.L) {
x -= (lli) c[A[L]] * (c[A[L]]-1);
c[A[L]]--;
x += (lli) c[A[L]] * (c[A[L]]-1);
L++;
}
while(R > q.R) {
x -= (lli) c[A[R]] * (c[A[R]]-1);
c[A[R]]--;
x += (lli) c[A[R]] * (c[A[R]]-1);
R--;
}
while(L > q.L) {
L--;
x -= (lli) c[A[L]] * (c[A[L]]-1);
c[A[L]]++;
x += (lli) c[A[L]] * (c[A[L]]-1);
}
while(R < q.R) {
R++;
x -= (lli) c[A[R]] * (c[A[R]]-1);
c[A[R]]++;
x += (lli) c[A[R]] * (c[A[R]]-1);
}
if(x == 0) {
X[q.id] = 0;
Y[q.id] = 1;
continue;
}
X[q.id] = x;
Y[q.id] = (lli)(q.R-q.L+1) * (q.R-q.L);
lli g = gcd(X[q.id], Y[q.id]);
X[q.id] /= g;
Y[q.id] /= g;
}
for(int i = 1; i <= m; i++) printf("%lld/%lld\n", X[i], Y[i]);
return 0;
}