BZOJ2038 小Z的袜子(莫队)

题意

BZOJ2038

题解

询问区间(L, R)选两只袜子颜色相同的概率

ans = C(a,2) + C(b,2) + C(c,2) + ... / C(R-L+1, 2)

找规律可得 (a^2 + b^2 + c^2  - a - b - c) / (R - L + 1)(R - L)

即(a^2 + b^2 + c^2 - (R - L + 1) ) / (R - L + 1)(R - L)

a, b, c为此种颜色在区间里的个数

那么a^2+b^2+c^2就是莫队要处理的区间值。

cnt[a]为删除的边界颜色个数,cnt[b]为增加边界颜色个数。

remove里面先减去颜色a之前的贡献cnt[a]^2再让其自减cnt[a]--再加上cnt[a]^2。

insert里面先减去颜色b之前的贡献cnt[b]^2再让其自加cnt[b]++再加上cnt[b]^2。

代码

#include<cstdio>
#include<algorithm>
#include <iostream>
#include <string.h>
#include <vector>
#include <queue>
#include <math.h>
using namespace std;

typedef long long ll;
const int maxn = 55555;
int block; //块的个数
struct Query {
    int i, l, r;
    bool operator < (const Query &q) const {
        if(l/block == q.l/block) return r < q.r;
        return l/block < q.l/block;
    }
}query[maxn];

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

int seq[maxn];
ll cnt[maxn], ansx[maxn], ansy[maxn];
void insert(ll &res, int a)
{
    res -= cnt[a]*cnt[a];
    ++cnt[a];
    res += cnt[a]*cnt[a];
}

void remove(ll &res, int a)
{
    res -= cnt[a]*cnt[a];
    --cnt[a];
    res += cnt[a]*cnt[a];
}
int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    block = sqrt(n);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &seq[i]);
    }
    for(int i = 0; i < m; i++)
    {
        query[i].i = i;
        scanf("%d%d", &query[i].l, &query[i].r);
    }
    sort(query, query+m);
    int l = 1, r = 1;
    ++cnt[seq[1]];
    ll res = 1;
    for(int i = 0; i < m; i++)
    {
        if(query[i].l == query[i].r) {
            ansx[query[i].i] = 0;
            ansy[query[i].i] = 1;
            continue;
        }
        while(l < query[i].l)
        {
            remove(res, seq[l]);
            ++l;
        }
        while(l > query[i].l)
        {
            --l;
            insert(res, seq[l]);
        }
        while(r < query[i].r)
        {
            ++r;
            insert(res, seq[r]);
        }
        while(r > query[i].r)
        {
            remove(res, seq[r]);
            --r;
        }
        ll a =  res - (query[i].r - query[i].l+1);
        ll b = (query[i].r - query[i].l + 1ll)*(query[i].r - query[i].l);
        ll c = gcd(b, a);
        ansx[query[i].i] = a/c;
        ansy[query[i].i] = b/c;
    }

    for(int i = 0; i < m; i++)
    {
        printf("%lld/%lld\n", ansx[i], ansy[i]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/QiHang_QiHang/article/details/81389150