题解:
这道题是莫队算法入门题之一,根据组合公式和每次都是只拿2个可知Cn2那么我们化简可以得到Cn2=n*(n-1)/2,那么我们可以在每次修改的时候减去之前的影响,再加上现在的影响就可以得到答案了,具体怎么写,看我代码吧,还有就是这道题要开LL,不然会教你做人。。。
题外话:
这道题别人轻易就过了,而我跳了很多坑。。。因为自己有点懒所以在关于l分块的写法上跟网上的有点不同,导致不断的出错, 后面经过不断的修改终于写出了AC的绿色了,让我很高兴,也学会了别人如何优秀的分块,而不是我之前那种有点挫的分块。。
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#define LL long long int
using namespace std;
const int MAXN=50000+7;
struct node
{
int l,r;
int id;
int pos;
}q[MAXN];
bool cmp(node c,node d)
{
if(c.pos==d.pos) return c.r<d.r;//同一个块时以r排序
return c.l<d.l;//不同块时以l排序
}
struct node2
{
LL x,y;
}ans[MAXN];
int a[MAXN],block[MAXN];
LL cnt[MAXN];
LL val;
LL gcd(LL x,LL y) { return x%y ? gcd(y, x%y) : y; }
void add(int x)
{
val-=cnt[a[x]]*(cnt[a[x]]-1);
cnt[a[x]]++;
val+=cnt[a[x]]*(cnt[a[x]]-1);
}
void del(int x)
{
val-=cnt[a[x]]*(cnt[a[x]]-1);
cnt[a[x]]--;
val+=cnt[a[x]]*(cnt[a[x]]-1);
}
int main()
{
// freopen("a.txt","r",stdin);
int n,m;
while(~scanf("%d%d",&n,&m))
{
memset(cnt,0,sizeof(cnt));
int s=sqrt(n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),block[i]=(i-1)/s+1;//如果写成i/s的话1-9就会出现,1,2为一块,3,4,5为一块,6,7,8为一块,而9为一块
for(int i=1;i<=m;i++)
scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i,q[i].pos=block[q[i].l];//这里的q[i].pos也可以写成q[i]=pos=q[i].l/s。
sort(q+1,q+m+1,cmp);
// for(int i=1;i<=m;i++)
// printf("%d %d %d\n",q[i].l,q[i].r,q[i].pos);
memset(cnt,0,sizeof(cnt));
int l=1,r=0;
val=0;
for(int i=1;i<=m;i++)
{
// printf("%d\n",i);
// if(q[i].l==q[i].r)
// {
// ans[q[i].id].x=0,ans[q[i].id].y=1;
// continue;
// }
while(l<q[i].l)
{
del(l);
l++;
}
while(l>q[i].l)
{
l--;
add(l);
}
while(r<q[i].r)
{
r++;
add(r);
}
while(r>q[i].r)
{
del(r);
r--;
}
LL temp=(LL)(q[i].r-q[i].l+1)*(q[i].r-q[i].l);//MD,这里忘记转化成LL被教做人,导致我找了一晚上,不断的WA,不断的怀疑人生,哎什么时候能改掉这个坏毛病
LL w=val;
LL g=gcd(w,temp);
temp/=g;
w/=g;
ans[q[i].id].x=w,ans[q[i].id].y=temp;
}
for(int i=1;i<=m;i++)
printf("%lld/%lld\n",ans[i].x,ans[i].y);
}
}