题目描述
有n个点,每个点有一种颜色,有m个询问,每个询问有l和r,求出在l~r的区间随机选两个点,两个点颜色相同的概率
思路
莫队
阿西巴不想写式子了
求概率的那个式子分子分解一下就能得出
(
∑
l
r
(
a
i
)
2
)
−
(
r
−
l
+
1
)
(\sum_{l}^{r}(a_i)^2) - (r - l + 1)
(∑lr(ai)2)−(r−l+1)
就可以边做边记录了
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define ll long long
using namespace std;
struct wh_
{
ll l, r, id, a, b;
}q[50250];
ll pos[50250], C[50250], A[50250];
ll n, m, nn, ans;
bool nW(wh_ i, wh_ j)
{
if(pos[i.l] == pos[j.l])
return i.r < j.r;
else return i.l < j.l;
}
bool nM(wh_ i, wh_ j)
{return i.id < j.id;}
void add(ll k, ll t)
{
ans -= C[A[k]] * C[A[k]];
C[A[k]] += t;
ans += C[A[k]] * C[A[k]];
}
ll gcd(ll a, ll b)
{return (b) ? gcd(b, a % b) : a;}
void init()
{
ans = 0;
ll l = 1, r = 0;
for(ll i = 1; i <= m; ++i)
{
if(q[i].l == q[i].r)
{
q[i].a = 0;
q[i].b = 1;
continue;
}
while(l < q[i].l)add(l, -1), l++;
while(l > q[i].l)add(l - 1, 1), l--;
while(r < q[i].r)add(r + 1, 1), r++;
while(r > q[i].r)add(r, -1), r--;
q[i].a = ans - (q[i].r - q[i].l + 1);
q[i].b = (q[i].r - q[i].l + 1) * (q[i].r - q[i].l);
ll g = gcd(q[i].a, q[i].b);
q[i].a /= g, q[i].b /= g;
}
return;
}
int main()
{
scanf("%lld%lld", &n, &m);
ll nn = (int)sqrt(n);
for(ll i = 1; i <= n; ++i)
{
scanf("%lld", &A[i]);
pos[i] = (i - 1) / nn + 1;
}
for(ll i = 1; i <= m; ++i)
{
scanf("%lld%lld", &q[i].l, &q[i].r);
q[i].id = i;
}
sort(q + 1, q + m + 1, nW);
init();
sort(q + 1, q + m + 1, nM);
for(ll i = 1; i <= m; ++i)
printf("%lld/%lld\n", q[i].a, q[i].b);
return 0;
}