「题解」洛谷 P1494 [国家集训队]小Z的袜子

题目

P1494 [国家集训队]小Z的袜子

思路

\(ans = \sum_{i = 1}^{n} p_i\)\(p_i\) 是拿到两根颜色为 \(i\) 的袜子的概率。

\(p_i = \dfrac{s_i \times (s_i - 1)}{len\times (len - 1)}\)\(s_i\) 是颜色 \(i\) 在区间中出现的次数,\(len\) 是区间长度。

用莫队维护出现次数,分子很容易算,分母是固定的,约分一下。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define M 50001

typedef long long ll;
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }

ll now, fz[M], fm[M];
int n, m, sqrn, a[M], num[M], cnt[M];
struct query {
    int x, y, len, id;
    friend bool operator < (query q1, query q2) {
        if (num[q1.x] == num[q2.x]) return q1.y < q2.y;
        return num[q1.x] < num[q2.x];
    }
}q[M];

void add(int x) {
    int temp = cnt[x]; ++cnt[x];
    now = now - 1ll * temp * (temp - 1) + 1ll * temp * cnt[x];
}

void del(int x) {
    int temp = cnt[x]; --cnt[x];
    now = now - 1ll * temp * (temp - 1) + 1ll * cnt[x] * (cnt[x] - 1);
}

int main() {
    scanf("%d %d", &n, &m), sqrn = n / sqrt(m);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        num[i] = (i - 1) / sqrn + 1;
    }
    for (int i = 1; i <= m; ++i) {
        scanf("%d %d", &q[i].x, &q[i].y);
        q[i].id = i, q[i].len = q[i].y - q[i].x + 1;
    }
    std::sort(q + 1, q + m + 1);
    int l = 1, r = 1; cnt[a[1]] = 1;
    for (int i = 1; i <= m; ++i) {
    	while (l > q[i].x) add(a[--l]);
        while (r < q[i].y) add(a[++r]);
        while (l < q[i].x) del(a[l++]);
        while (r > q[i].y) del(a[r--]);
        if (now == 0) fz[q[i].id] = 0, fm[q[i].id] = 1;
        else {
            ll g = gcd(now, 1ll * q[i].len * (q[i].len - 1));
            fz[q[i].id] = now / g, fm[q[i].id] = 1ll * q[i].len * (q[i].len - 1) / g;
        }
    }
    for (int i = 1; i <= m; ++i) {
        if (fz[i] == 0) fm[i] = 1;
        printf("%lld/%lld\n", fz[i], fm[i]);
    }
    return 0;
}
posted @ 2020-10-30 16:07  yu__xuan  阅读(65)  评论(3编辑  收藏  举报