【题解】
本题中,每个区间的答案不能由子问题合并得到,所以不能使用分治一类的数据结构如线段树等
而 ans = sigma( cnt[color[i]]*(cnt[color[i]]-1)/2 ),构成答案的颜色不止一种,所以要将不同颜色分开算答案
但 n<=50000,对于每个询问,仅枚举颜色就超时了,只能从位置的角度考虑
我这个蒟蒻只能写分块法。
分块的对象:由于区间的答案不能由子问题合并得到,所以对序列分块无效,考虑对询问分块
考虑将询问按L排序并固定左端点,枚举右端点,记录途经颜色个数的暴力:对于每个L,仅L位置当然是O(1)的,枚举R位置是O(n)的
它的时间浪费在:对同一位置的重复枚举
其实这个暴力与分块是有联系的:
它相当于将询问按左端点分块,块的大小size=1,每个块内可以将R从小到大排序,O(n)顺推过去求答案
那么,考虑分块的一般情况:块的大小为size,则:
同一块内的每个L,都要用size时间枚举答案,而R只需整体O(n)顺推,增减答案。总时间:枚举L:n*size,枚举R:n/size*n
将size取为sqrt(n),则程序总时间复杂度为:O( n*sqrt(n) )
【代码】
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
typedef long long LL;
LL ans[50005];
int col[50005],bel[50005],L[50005],R[50005],id[50005],cnt[50005];
void jh(int* a,int* b)
{
int t=*a;
*a=*b;
*b=t;
}
void kp_L(int low,int high)
{
int i=low,j=high,mid=L[(i+j)/2];
while(i<j)
{
while(L[i]<mid) i++;
while(L[j]>mid) j--;
if(i<=j)
{
jh(&L[i],&L[j]);
jh(&R[i],&R[j]);
jh(&id[i],&id[j]);
i++;
j--;
}
}
if(j>low) kp_L(low,j);
if(i<high) kp_L(i,high);
}
void kp_R(int low,int high)
{
int i=low,j=high,mid=R[(i+j)/2];
while(i<j)
{
while(R[i]<mid) i++;
while(R[j]>mid) j--;
if(i<=j)
{
jh(&L[i],&L[j]);
jh(&R[i],&R[j]);
jh(&id[i],&id[j]);
i++;
j--;
}
}
if(j>low) kp_R(low,j);
if(i<high) kp_R(i,high);
}
void kp_id(int low,int high)
{
int i=low,j=high,mid=id[(i+j)/2];
while(i<j)
{
while(id[i]<mid) i++;
while(id[j]>mid) j--;
if(i<=j)
{
jh(&L[i],&L[j]);
jh(&R[i],&R[j]);
jh(&id[i],&id[j]);
i++;
j--;
}
}
if(j>low) kp_id(low,j);
if(i<high) kp_id(i,high);
}
LL C(int x)
{
return (LL)x*((LL)x-1LL)/2LL;
}
LL gcd(LL a,LL b)
{
if(b==0) return a;
return gcd(b,a%b);
}
int main()
{
LL t,g;
int n,m,i,size,from=1,to,l,r;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",&col[i]);
for(i=1;i<=m;i++)
{
scanf("%d%d",&L[i],&R[i]);
id[i]=i;
}
kp_L(1,m);
size=(int)sqrt(n);
for(i=1;i<=n;i++)
bel[i]=(i-1)/size+1;
for(to=1;to<=m;to++)
if(bel[L[to+1]]!=bel[L[from]])//内部允许O(n)的算法
{
memset(cnt,0,sizeof(cnt));
kp_R(from,to);
l=L[from]+1;
r=L[from];
for(i=from;i<=to;i++)
{
if(i>from) ans[id[i]]=ans[id[i-1]];
while(r<R[i])
{
//printf("r=%d ",r);
ans[id[i]]-=C(cnt[col[++r]]);
ans[id[i]]+=C(++cnt[col[r]]);
}
while(l<L[i])
{
ans[id[i]]-=C(cnt[col[l]]--);
ans[id[i]]+=C(cnt[col[l++]]);
}
while(l>L[i])
{
ans[id[i]]-=C(cnt[col[--l]]);
ans[id[i]]+=C(++cnt[col[l]]);
}
}
from=to+1;
}
kp_id(1,m);
for(i=1;i<=m;i++)
{
if(ans[i]==0) printf("0/1\n");
else
{
t=C(R[i]-L[i]+1);
g=gcd(ans[i],t);
printf("%lld/%lld\n",ans[i]/g,t/g);
}
}
return 0;
}