题解 P1494 【[国家集训队]小Z的袜子】
获得更好的阅读体验
题面
很容易看出来是莫队的题.
题目分析
给出
概率很好算,只要求出区间内每个数字出现的次数,用次数来求出能取出的方案数,以及整个区间能够取得方案数就可以了.
题目要求通分,用欧几里得算法求
函数如下:
inline lxl gcd(lxl x,lxl y)
{
return y?gcd(y,x%y):x;
}
然后同志们再来考虑如何使用莫队进行统计.
因为是在区间内取出两个数,设某个数字出现的次数为
b_1=a\*(a-1)/2
在删去一个数时,
b_2=(a-1)\*(a-2)/2
展开,可以得出:
b_2=b_1-(a-1)
同理,在加上一个数时,可以得到:
b_2=b_1+a
所以我们就可以写出莫队的转移函数了:
inline void delete_(lxl id)
{
tot[num[id]]--;
if(tot[num[id]]>0)
{
cnt=cnt-tot[num[id]];
}
}
inline void add(lxl id)
{
tot[num[id]]++;
if(tot[num[id]]>1)
{
cnt=cnt+(tot[num[id]]-1);
}
}
其他内容也就是一个寻常的莫队了.
代码
#include "cstdio"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "iostream"
#define lxl long long
#define debug(x) printf("debug:%lld\n",x)
using namespace std;
lxl n,m,len,CurL,CurR,cnt;
lxl tot[500010],num[500010],ans[500010],QLen[500010];
struct QUERY
{
lxl l,r,id;
}q[50010];
inline bool cmp(QUERY d1,QUERY d2)
{
return (d1.l/len==d2.l/len)?d1.r<d2.r:d1.l<d2.l;
}
inline lxl clac(lxl);
inline void delete_(lxl);
inline void add(lxl);
inline lxl gcd(lxl,lxl);
signed main(void)
{
// freopen("testdata.in","r",stdin);
// freopen("test.out","w",stdout);
scanf("%lld%lld",&n,&m);
len=n/sqrt(m*2/3);
for(lxl _=1;_<=n;_++)
{
scanf("%lld",num+_);
}
for(lxl _=1;_<=m;_++)
{
lxl l,r;
scanf("%lld%lld",&l,&r);
QLen[_]=r-l+1;
q[_].l=l;
q[_].r=r;
q[_].id=_;
// debug(QLen[_]);
}
sort(q+1,q+1+m,cmp);
/* for(lxl _=1;_<=m;_++)
{
debug(q[_].id);
}*/
CurL=CurR=q[1].l;
tot[num[CurL]]++;
for(lxl _=1;_<=m;_++)
{
lxl l=q[_].l;
lxl r=q[_].r;
lxl IDQuery=q[_].id;
while(CurL<l)
{
delete_(CurL++);
// debug(CurL);
// debug(cnt);
}
while(CurL>l)
{
add(--CurL);
// debug(CurL);
// debug(cnt);
}
while(CurR<r)
{
add(++CurR);
// debug(CurR);
// debug(cnt);
}
while(CurR>r)
{
delete_(CurR--);
// debug(CurR);
// debug(cnt);
}
ans[IDQuery]=cnt;
}
for(lxl _=1;_<=m;_++)
{
if(ans[_]==0||QLen[_]==1)
{
printf("0/1\n");
continue;
}
lxl len_=QLen[_]*(QLen[_]-1)/2;
lxl gcd_=gcd(ans[_],len_);
printf("%lld/%lld\n",ans[_]/gcd_,len_/gcd_);
}
return 0;
}
/*
inline lxl clac(lxl id)
{
if(tot[num[id]]==0||tot[num[id]]==1)
{
return 0;
}
return tot[num[id]]*(tot[num[id]]-1)/2;
}
*/
inline void delete_(lxl id)
{
tot[num[id]]--;
if(tot[num[id]]>0)
{
cnt=cnt-tot[num[id]];
}
}
inline void add(lxl id)
{
tot[num[id]]++;
if(tot[num[id]]>1)
{
cnt=cnt+(tot[num[id]]-1);
}
}
inline lxl gcd(lxl x,lxl y)
{
return y?gcd(y,x%y):x;
}