[题解]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;
}``
posted @ 2020-08-27 10:01  marTixx  阅读(91)  评论(0编辑  收藏  举报