这是中南第二届邀请赛的题目,当时还不懂莫队算法,,现在做起来感觉思路还是挺清晰的,可以用来当莫队算法的练习
首先讲下莫队算法(也是逆天),是一种分块的思路(超逆天思维)
它是一种离线算法,复杂度是O(m*sqrt(n)),m是区间范围大小,n是区间个数
适用条件:如果知道[l,r]的答案,可以用O(1)或者O(logn)的复杂度求出[l-1,r] [l+1,r] [l,r-1] [l,r+1]的答案,那么就可以用莫队算法(几乎是区间万能算法)
大概的思路是,把n个区间分成sqrt(n)块,求出区间左端点所在的块,然后排序
先按左端点所在的块的大小排序,如果相等,再按照右端点的大小排序。如果右端点的大小相等,再按照左端点的大小排序
然后只要按照排序的顺序,依次求出每个区间的答案就可以了
莫队算法复杂度证明:
设n为区间个数,m为r最大的值
y向右移动的次数,最极端的情况下,每个块的y都会从1移动到m,一共有sqrt(n)个块,所以这的复杂度是m*sqrt(n)
y向左移动的次数,只会在跨块的时候才会出现,一共只会出现sqrt(n)-1次跨块的情况,假如每次y都是从n向左移动到了1,所以这的复杂度是m*sqrt(n)
x不跨块移动的次数,l会在1和这个区间长度的交换,记当前区间长度为w,一共sqrt(n)个块,一个块有sqrt(n)个,复杂度w*sqrt(n)*n=w*n,实际上m平均等于sqrt(m)左右,总的复杂度n*sqrt(m)
x跨块移动,跨块只有sqrt(n)-1种情况,每次跨块最多移动两个区间长<m,所以复杂度<m*sqrt(n)
综上所述,总的复杂度O(m*sqrt(n))
分块法有很多逆天的作用,莫队算法只是其中的一种用法。
#include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<vector> #include<queue> #include<algorithm> using namespace std; const int HS=1000007; const int MX=100000+5; typedef long long LL; int num[MX]; int m,n,unit; LL A[MX],ans[MX]; int Head[HS],Next[MX]; void hash_create(){ memset(Head,-1,sizeof(Head)); memset(Next,-1,sizeof(Next)); memset(num,0,sizeof(num)); for(int i=1;i<=n;i++){ int h=A[i]%HS,sign=true; for(int j=Head[h];j!=-1;j=Next[j]){ if(A[i]==A[j]){ sign=false; break; } } if(sign){ Next[i]=Head[h]; Head[h]=i; } } } int query(LL x){ int h=x%HS; for(int i=Head[h];i!=-1;i=Next[i]){ if(A[i]==x) return num[i]; } return 0; } void update(LL x,int d){ int h=x%HS; for(int i=Head[h];i!=-1;i=Next[i]){ if(A[i]==x){ num[i]+=d; return; } } } struct Query{ int l,r,id; bool operator<(const Query&b)const{ if(l/unit==b.l/unit){ if(r==b.r) return l<b.l; return r<b.r; } return l/unit<b.l/unit; } }Q[MX]; void work(){ LL S=0; int L=1,R=0; for(int i=1;i<=m;i++){ while(L<Q[i].l){ S-=query(A[L]-1)+query(A[L]+1); update(A[L],-1); L++; } while(L>Q[i].l){ L--; S+=query(A[L]-1)+query(A[L]+1); update(A[L],1); } while(R<Q[i].r){ R++; S+=query(A[R]-1)+query(A[R]+1); update(A[R],1); } while(R>Q[i].r){ S-=query(A[R]-1)+query(A[R]+1); update(A[R],-1); R--; } ans[Q[i].id]=S; } } int main(){ //freopen("input.txt","r",stdin); while(~scanf("%d%d",&n,&m)){ for(int i=1;i<=n;i++){ scanf("%lld",&A[i]); } hash_create(); for(int i=1;i<=m;i++){ scanf("%d%d",&Q[i].l,&Q[i].r);Q[i].id=i; } unit=sqrt(n+0.5); sort(Q+1,Q+1+m); work(); for(int i=1;i<=m;i++){ printf("%lld\n",ans[i]); } } return 0; }