莫队算法+哈希 csu1515 Sequence

这是中南第二届邀请赛的题目,当时还不懂莫队算法,,现在做起来感觉思路还是挺清晰的,可以用来当莫队算法的练习


首先讲下莫队算法(也是逆天),是一种分块的思路(超逆天思维)

它是一种离线算法,复杂度是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;
}

你可能感兴趣的:(csu1515)