莫队算法&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; }