题目大意:
题目链接:
洛谷:https://www.luogu.org/problemnew/show/P1494
BZOJ:https://www.lydsy.com/JudgeOnline/problem.php?id=2038
有个询问,每次询问从中选择两个元素相同的概率是多少。
思路:
- 莫队算法
这道题是莫队的模板题。
莫队算法其实是很简单的。码量也不算大。可以做到离线处理一些区间问题。
莫队的核心思想就是将询问排序,每次区间的答案就从转移来。
网上的讲解大部分都比较敷衍,这里就给出一篇比较好的讲解吧 链接
代码:
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=50010;
int n,m,T,l,r,a[N],pos[N];
ll s[N],ans;
struct Ask
{
int l,r,id;
ll ans1,ans2;
}ask[N];
bool cmp1(Ask x,Ask y)
{
if (pos[x.l]<pos[y.l]) return 1;
if (pos[x.l]>pos[y.l]) return 0;
return x.r<y.r;
}
bool cmp2(Ask x,Ask y)
{
return x.id<y.id;
}
void add(int x)
{
ans-=s[a[x]]*(s[a[x]]-1)/2;
if (x) s[a[x]]++;
ans+=s[a[x]]*(s[a[x]]-1)/2;
}
void dev(int x)
{
ans-=s[a[x]]*(s[a[x]]-1)/2;
if (x) s[a[x]]--;
ans+=s[a[x]]*(s[a[x]]-1)/2;
}
int main()
{
scanf("%d%d",&n,&m);
T=(int)sqrt(m);
if (m%T) T++;
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&ask[i].l,&ask[i].r);
ask[i].id=i;
pos[i]=(i-1)/T+1;
}
sort(ask+1,ask+1+m,cmp1);
for (int i=1;i<=m;i++)
{
for (;l<ask[i].l;l++) dev(l);
for (;l>ask[i].l;l--) add(l-1);
for (;r<ask[i].r;r++) add(r+1);
for (;r>ask[i].r;r--) dev(r);
if (l==r) ask[i].ans1=0,ask[i].ans2=1;
else
{
ll k=ask[i].r-ask[i].l+1;
ll a1=ans,a2=k*(k-1)/2;
ll GCD=__gcd(a1,a2);
ask[i].ans1=a1/GCD;
ask[i].ans2=a2/GCD;
}
}
sort(ask+1,ask+1+m,cmp2);
for (int i=1;i<=m;i++)
printf("%lld/%lld
",ask[i].ans1,ask[i].ans2);
return 0;
}