传送门
I think
题意:给出长度为n的区间与若干形如[l,r]的区间,询问在[l,r]内两个不同位置取到两个相同的数的概率。
算法:莫队
思想:将组合数求概率的式子化简一下:
(col 表示区间内颜色总数 f[i]表示区间内颜色出现次数)
于是我们可以看伟大的黄学长题解
莫队算法
如果我们已知[l,r]的答案,能在O(1)时间得到[l+1,r]的答案以及[l,r-1]的答案,即可使用莫队算法。时间复杂度为O(n^1.5)。如果只能在logn的时间移动区间,则时间复杂度是O(n^1.5*log n)。
其实就是找一个数据结构支持插入、删除时维护当前答案。
这道题的话我们很容易用数组来实现,做到O(1)的从[l,r]转移到[l,r+1]与[l+1,r]。
那么莫队算法怎么做呢?以下都是在转移为O(1)的基础下讨论的时间复杂度。另外由于n与m同阶,就统一写n。
如果已知[l,r]的答案,要求[l’,r’]的答案,我们很容易通过|l – l’|+|r – r’|次转移内求得。
将n个数分成sqrt(n)块。
按区间排序,以左端点所在块内为第一关键字,右端点为第二关键字,进行排序,也就是以(pos [l],r)排序
然后按这个排序直接暴力,复杂度分析是这样的:
1、i与i+1在同一块内,r单调递增,所以r是O(n)的。由于有n^0.5块,所以这一部分时间复杂度是n^1.5。
2、i与i+1跨越一块,r最多变化n,由于有n^0.5块,所以这一部分时间复杂度是n^1.5
3、i与i+1在同一块内时l变化不超过n^0.5,跨越一块也不会超过n^0.5,忽略*2。由于有m次询问(和n同级),所以时间复杂度是n^1.5
于是就是O(n^1.5)了
最后最小公倍数约一下分子分母就可以输出答案了。
Code
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int sm = 50000+10;
LL Ans;
int N,M,Blk;
int S[sm],C[sm],pos[sm];
struct Que {
int id,l,r;
LL a,b;
}A[sm];
LL Gcd(LL a,LL b) { return b == 0?a:Gcd(b,a%b); }
LL ind(int x) { return 1ll*x*x; }
void read(int &x) {
char ch=getchar();x=0;
while(ch>'9'||ch<'0') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void Out(LL a) {
if(a>9) Out(a/10);
putchar('0'+a%10);
}
bool cmpa(Que x,Que y) {
return (pos[x.l]!=pos[y.l])?pos[x.l]<pos[y.l]:x.r<y.r;
}
bool cmpb(Que x,Que y) { return x.id<y.id; }
void Update(int p,int add) {
Ans-=ind(S[C[p]]);
S[C[p]]+=add;
Ans+=ind(S[C[p]]);
}
void Solve() {
int l=1,r=0;//注意l=1的细节
LL k;
for(int i=1;i<=M;++i) {
for(;r<A[i].r;++r) Update(r+1,1);
for(;r>A[i].r;--r) Update(r,-1);
for(;l<A[i].l;++l) Update(l,-1);
for(;l>A[i].l;--l) Update(l-1,1);
if(A[i].l==A[i].r) {
A[i].a=0,A[i].b=1;
continue;
}
A[i].a=Ans-(A[i].r-A[i].l+1);
A[i].b=1ll*(A[i].r-A[i].l+1)*(A[i].r-A[i].l);
k=Gcd(A[i].a,A[i].b);
A[i].a/=k,A[i].b/=k;
}
}
int main() {
read(N),read(M);
Blk=sqrt(N);
for(int i=1;i<=N;++i)
read(C[i]),pos[i]=(i-1)/Blk+1;
for(int i=1;i<=M;++i)
read(A[i].l),read(A[i].r),A[i].id=i;
sort(A+1,A+M+1,cmpa);
Solve();
sort(A+1,A+M+1,cmpb);
for(int i=1;i<=M;++i) {
Out(A[i].a),putchar('/');
Out(A[i].b),putchar(10);
}
return 0;
}