题意
题解
询问区间(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;
}