给定序列a[1]~a[n],a[i]表示i的颜色,多次询问区间L~R中选出两个位置颜色相同的概率
N,Q<=50000.
若我们已知L~R区间出现的颜色为c1~ck,次数cnt1~cntk
概率表达式?
若我们已知[L,R]的信息(cnt,P),我们可以O(1)将它更新为[L+1,R]/[L-1,R]/[L,R-1]/[L,R+1].此即为莫队算法。
我们将询问以[(l-1)/sqrt(N)]为第一关键字,R为第二关键字排序,我们暴力求出第一个询问的答案,然后对于以后的询问答案由之前的询问进行更新
时间复杂度?
右端点O(N*sqrt(N))
左端点O(N)
总时间复杂度O(N*sqrt(N))
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define ll long long
#define N 50000
using namespace std;
int n,m,A[N+5],block;
ll ans=0,f[N];
struct query{
int l,r,id,block;
}data[N+5];
struct ANs{
ll a,b;
}ANS[N];
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
bool cmp(query x,query y){
return x.block==y.block? x.r<y.r:x.block<y.block;
}
void update(int x,int op){
ans-=f[A[x]]*(f[A[x]]-1);
f[A[x]]+=op;
ans+=f[A[x]]*(f[A[x]]-1);
}
inline ll gcd(ll x,ll y){
return y==0?x:gcd(y,x%y);
}
int main(){
freopen("a.in","r",stdin);
n=read();m=read();block=sqrt(n);
for(int i=1;i<=n;++i) A[i]=read();
for(int i=1;i<=m;++i){
data[i].l=read();data[i].r=read();data[i].id=i;
data[i].block=(data[i].l-1)/block;
}
sort(data+1,data+m+1,cmp);
int l=1,r=0;//l一定要从1开始,r从0开始
ll a=0,b=0;
for(int i=1;i<=m;++i){
for(;l>data[i].l;--l) update(l-1,1);
for(;l<data[i].l;++l) update(l,-1);
for(;r<data[i].r;++r) update(r+1,1);
for(;r>data[i].r;--r) update(r,-1);
if(data[i].l==data[i].r){
ANS[data[i].id].a=0;
ANS[data[i].id].b=1;continue;
}
a=ans;b=(ll)(data[i].r-data[i].l+1)*(data[i].r-data[i].l);
ll k=gcd(a,b);
ANS[data[i].id].a=a/k;ANS[data[i].id].b=b/k;
}
for(int i=1;i<=m;++i)
printf("%lld/%lld\n",ANS[i].a,ANS[i].b);
return 0;
}