题目来源:
https://www.luogu.org/problemnew/listsname=%E5%B0%8FZ%E7%9A%84%E8%A2%9C%E5%AD%90
题意:中文题简单说一下吧,首先给定两个数n,m分别代表袜子的数量与查询的次数,接下来一行有n个数代表袜子的颜色,数字相同代表颜色相同,每次查询给定两个数字代表查询的区间。
分析:经典莫队离线,刚开始接触莫队,看了好多种板子,这是自己敲的第一个题,要求输出的是概率(最简),所以要有一个求最大公约数(辗转相除),分母C(n,2),n是(r-l+1),分子是区间内相同颜色的袜子的总量m(不是所有颜色的总量,只是一种颜色的总量),C(m,2),所有满足条件的颜色加起来就是分子,add与del里面用了等差数列代替了C(m,2),代码处给了注释。
代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
ll a[50010]={0};
ll vis[50010]={0};
ll block,sum=0;
struct node
{
ll l,r;
ll id;
}b[50010];
struct n
{
ll w,t;
}ans[50010];
bool cmp(node p,node q)
{
if(p.l/block==q.l/block)
return p.r<q.r;
else
return p.l<q.l;
}
void add(ll x)
{
vis[a[x]]++;
if(vis[a[x]]>=2)
sum+=2*(vis[a[x]]-1);//等差数列2//下面的分母的2 * {n*(n-1)/2}//Cn2;
}
void del(ll x)
{
vis[a[x]]--;
if(vis[a[x]]>=1)
sum-=2*vis[a[x]];
}
ll gcd(ll x,ll y)
{
ll r;
while(y)
{
r=x%y;
x=y;
y=r;
}
return x;
}
int main()
{
ll n,m;
scanf("%lld %lld",&n,&m);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(ll i=1;i<=m;i++)
{
scanf("%lld %lld",&b[i].l,&b[i].r);
b[i].id=i;
}
block=sqrt(n);
sort(b+1,b+m+1,cmp);
ll tl=1,tr=0;
for(ll i=1;i<=m;i++)
{
if(b[i].l==b[i].r)
{
ans[b[i].id].w=0;
ans[b[i].id].t=1;
continue;
}
while(tl<b[i].l)
del(tl++);
while(tl>b[i].l)
add(--tl);
while(tr<b[i].r)
add(++tr);
while(tr>b[i].r)
del(tr--);
ll pp=(b[i].r-b[i].l+1)*(b[i].r-b[i].l);
ll ss=gcd(pp,sum);
ans[b[i].id].w=sum/ss,ans[b[i].id].t=pp/ss;
}
for(ll i=1;i<=m;i++)
printf("%lld/%lld\n",ans[i].w,ans[i].t);
return 0;
}