【题解】
本题中,每个区间的答案不能由子问题合并得到,所以不能使用分治一类的数据结构如线段树等
而 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; }