莫队算法&BZOJ2038

题目传送门

今天看了分块,顺便把基本的莫队学习了一下。

莫队算法是一种离线算法,复杂度可以达到O((M+N)*Sqrt(N));

对于询问区间的左端点分块,块内的右端点从小到大排列。

对区间进行偏移操作。

掌握一个思想基础:两个询问之间的状态跳转。如图,当前完成的询问的区间为[a,b],下一个询问的区间为[p,q],现在保存[a,b]区间内的每个颜色出现次数的sum[]数组已经准备好,[a,b]区间询问的答案Ans1已经准备好,怎样用这些条件求出[p,q]区间询问的Ans2?

考虑指针向左或向右移动一个单位,我们要付出多大的代价才能维护sum[]和Ans(即使得sum[],Ans保存的是当前[l,r]的正确信息)。我们美妙地对图中l,r的向右移动一格进行分析:

l指针向右移动一个单位,所造成的后果就是:我们损失了一个绿色方块。那么怎样维护?美妙地,sum[绿色]减去1。那Ans如何维护?先看分母,分母从n2变成(n-1)2,分子中的其他颜色对应的部分是不会变的,绿色却从sum[绿色]2变成(sum[绿色]-1)2 ,为了方便计算我们可以直接向给Ans减去以前该颜色的答案贡献(即sum[绿色]2)再加上现在的答案贡献(即(sum[绿色]-1)2 )。同理,观赏下面的r指针移动,将是差不多的。

复杂度分析:

右端点偏移复杂度不受询问影响,为O(N*Sqrt(N));

左端点偏移复杂度:因为块的大小为Sqrt(N),所以一次操作最多偏移Sqrt(N),复杂度为O(M*Sqrt(N));

code:

/**************************************************************
    Problem: 2038
    User: yekehe
    Language: C++
    Result: Accepted
    Time:1756 ms
    Memory:4256 kb
****************************************************************/
 
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
 
char tc()
{
    static char tr[100000],*A=tr,*B=tr;
    return A==B&&(B=(A=tr)+fread(tr,1,100000,stdin),A==B)?EOF:*A++;
}
 
int read()
{
    char c;while(c=tc(),c<'0'||c>'9');
    int x=c-'0';while(c=tc(),c>='0'&&c<='9')x=x*10+c-'0';
    return x;
}
 
long long gcd(long long x,long long y){return !y?x:gcd(y,x%y);}
 
long long N,Q,sum[50005],c[50005],ans=0;
int B[50005],size;
struct Qr{long long x,y,id;}Qs[50005],A[50005];
int i,j;
inline int cmp(Qr x,Qr y){
    return B[x.x]==B[y.x]?x.y<y.y:x.x<y.x;
}
 
void updata(int x)
{
    ans=ans-sum[c[x]]*sum[c[x]];
    sum[c[x]]++;
    ans=ans+sum[c[x]]*sum[c[x]];
}
 
void remove(int x)
{
    ans=ans-sum[c[x]]*sum[c[x]];
    sum[c[x]]--;
    ans=ans+sum[c[x]]*sum[c[x]];
}
 
int main()
{
//  freopen("x.txt","r",stdin);
    N=read(),Q=read();
    size=sqrt(Q);size+=(size*size<Q);
    long long x,y;
        for(i=1;i<=N;i++)c[i]=read();
        for(i=1;i<=Q;i++)B[i]=(i-1)/size+1;
        for(i=1;i<=Q;i++)x=read(),y=read(),Qs[i]=(Qr){x,y,i};
    sort(Qs+1,Qs+Q+1,cmp);
    int l=1,r=0;
    long long len,As;
        for(i=1;i<=Q;i++){
            while(l>Qs[i].x)updata(--l);
            while(l<Qs[i].x)remove(l++);
            while(r>Qs[i].y)remove(r--);
            while(r<Qs[i].y)updata(++r);
            len=Qs[i].y-Qs[i].x+1;
            x=ans-len,y=len*(len-1);
            if(!x || Qs[i].x==Qs[i].y)
                {A[Qs[i].id].x=0,A[Qs[i].id].y=1;continue;}
            As=gcd(x,y);x/=As,y/=As;
            A[Qs[i].id].x=x,A[Qs[i].id].y=y;
        }
        for(i=1;i<=Q;i++)
            printf("%lld/%lld\n",A[i].x,A[i].y);
    return 0;
}

 

posted @ 2018-03-16 15:21  Cptraser  阅读(188)  评论(0编辑  收藏  举报