【BZOJ 2038】小Z的袜子 (莫队算法)

来源:互联网 发布:专业生物英语软件 编辑:程序博客网 时间:2024/06/11 17:54

传送门

BZOJ 2038 小Z的袜子

I think

    题意:给出长度为n的区间与若干形如[l,r]的区间,询问在[l,r]内两个不同位置取到两个相同的数的概率。
    算法:莫队
    思想:将组合数求概率的式子化简一下:

P()=coli=1C2f[i]C2rl+1=coli=1C2f[i](rl+1)(rl)2

(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;}
原创粉丝点击