莫队算法总结

来源:互联网 发布:sql server 官网 编辑:程序博客网 时间:2024/05/17 06:34

       莫队算法主要是利用了曼哈顿距离的长度关系,将每一个询问的坐标抽象为一个点,然后将询问分块,进行排序(分块的原因是使得这些询问坐标的移动的曼哈顿距离达到 最小),排序之后再按照此时的顺序进行左右区间的移动,而在内部的实现的过程就要看各个题目的要求了,所以在这里主要是理解到,莫队算法的核心是:平面上移动曼哈顿距离最小 (利用分块求得平面上曼哈顿距离的最小生成树)+ 离线查询(无法在线查询),在解题的过程中也要想到使得每一次转移计算答案的时间也要是O(1)的,到此,莫队算法也就完成。

三个样例:

HH的项链

(询问区间内不同数的个数,temp记录答案,只要num[arr[x]]>0那么temp+=1)

HYSBZ oj 1878

HH有一串由各种漂亮的贝壳组成的项链。HH相信不同的贝壳会带来好运,所以每次散步 完后,他都会随意取出一
段贝壳,思考它们所表达的含义。HH不断地收集新的贝壳,因此他的项链变得越来越长。有一天,他突然提出了一
个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答。。。因为项链实在是太长了。于是,他只
好求助睿智的你,来解决这个问题。
Input
第一行:一个整数N,表示项链的长度。 
第二行:N个整数,表示依次表示项链中贝壳的编号(编号为0到1000000之间的整数)。 
第三行:一个整数M,表示HH询问的个数。 
接下来M行:每行两个整数,L和R(1 ≤ L ≤ R ≤ N),表示询问的区间。
N ≤ 50000,M ≤ 200000。
Output

M行,每行一个整数,依次表示询问对应的答案。

Sample Input
61 2 3 4 3 531 2 3 52 6
Sample Output
224

#include <iostream>#include <stdio.h>#include <string.h>#include <algorithm>#include <math.h>#define siz 50005using namespace std;struct Query{    int L,R,id;}node[siz*4];int arr[siz],ans[siz*4],num[siz*20];int n,m,unit;bool cmp(Query a,Query b){    if(a.L/unit!=b.L/unit) return a.L/unit<b.L/unit;    return a.R<b.R;}void solve(){    int temp=0;    memset(num,0,sizeof(num));    int L=1,R=0;    for(int i=0;i<m;i++){       /// cout<<node[i].L<<" "<<node[i].R<<endl;        while(R<node[i].R){            R++;            if(num[arr[R]]!=0)            temp-=1;            num[arr[R]]++;            if(num[arr[R]]!=0)            temp+=1;           // cout<<arr[R]<<"---111---"<<num[arr[R]]<<endl;        }        while(R>node[i].R){            //R++;            if(num[arr[R]]!=0)            temp-=1;            num[arr[R]]--;            if(num[arr[R]]!=0)            temp+=1;          //  cout<<arr[R]<<"---222---"<<num[arr[R]]<<endl;            --R;        }        while(L<node[i].L){           /// R++;           if(num[arr[L]]!=0)            temp-=1;            num[arr[L]]--;            if(num[arr[L]]!=0)            temp+=1;            //cout<<arr[L]<<"---333---"<<num[arr[L]]<<endl;            L++;        }        while(L>node[i].L){            L--;            if(num[arr[L]]!=0)            temp-=1;            num[arr[L]]++;            if(num[arr[L]]!=0)            temp+=1;           // cout<<arr[L]<<"---444---"<<num[arr[L]]<<endl;        }        ans[node[i].id]=temp;    }    //cout<<temp<<endl;    for(int i=0;i<m;i++){        printf("%d\n",ans[i]);    }}int main(){    while(~scanf("%d",&n)){        for(int i=1;i<=n;i++){            scanf("%d",&arr[i]);        }        unit=(int)sqrt(n);        scanf("%d",&m);        for(int i=0;i<m;i++){            node[i].id=i;            scanf("%d %d",&node[i].L,&node[i].R);        }        sort(node,node+m,cmp);        solve();    }    return 0;}

小Z的袜子(hose)

(邝斌巨巨模板,不解释了,用这道题学的 莫队算法,另外两道是自己写的)

作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……
具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。
你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。

Input

输入文件第一行包含两个正整数N和M。N为袜子的数量,M为小Z所提的询问的数量。接下来一行包含N个正整数Ci,其中Ci表示第i只袜子的颜色,相同的颜色用相同的数字表示。再接下来M行,每行两个正整数L,R表示一个询问。

Output

包含M行,对于每个询问在一行中输出分数A/B表示从该询问的区间[L,R]中随机抽出两只袜子颜色相同的概率。若该概率为0则输出0/1,否则输出的A/B必须为最简分数。(详见样例)

Sample Input
6 4
1 2 3 3 3 2
2 6
1 3
3 5
1 6
Sample Output
2/5
0/1
1/1
4/15
【样例解释】询问1:共C(5,2)=10种可能,其中抽出两个2有1种可能,抽出两个3有3种可能,概率为(1+3)/10=4/10=2/5。
询问2:共C(3,2)=3种可能,无法抽到颜色相同的袜子,概率为0/3=0/1。
询问3:共C(3,2)=3种可能,均为抽出两个3,概率为3/3=1/1。注:上述C(a, b)表示组合数,组合数C(a, b)等价于在a个不同的物品中选取b个的选取方案数。
【数据规模和约定】30%的数据中 N,M ≤ 5000;60%的数据中 N,M ≤ 25000;100%的数据中 N,M ≤ 50000,1 ≤ L < R ≤ N,Ci ≤ N。

#include <iostream>#include <stdio.h>#include <string.h>#include <algorithm>#include <math.h>#define siz 50005#define LL long longusing namespace std;struct Query{    int L,R,id;}node[siz];LL gcd(LL a,LL b){    if(b==0) return a;    else return gcd(b,a%b);}struct Ans{    LL a,b;    void reduce(){        LL d=gcd(a,b);        a/=d,b/=d;    }}ans[siz];int a[siz],num[siz];int n,m,unit;bool cmp(Query a,Query b){    if(a.L/unit!=b.L/unit) return a.L/unit<b.L/unit;    else return a.R<b.R;}void work(){    LL temp=0;    memset(num,0,sizeof(num));    int L=1,R=0;    for(int i=0;i<m;i++){        while(R<node[i].R){            R++;            //cout<<L<<"--111---"<<R<<endl;            temp-=(LL) num[a[R]]*num[a[R]];            num[a[R]]++;            temp+=(LL) num[a[R]]*num[a[R]];        }        while(R>node[i].R){            //R++;            //cout<<L<<"--222---"<<R<<endl;            temp-=(LL) num[a[R]]*num[a[R]];            num[a[R]]--;            temp+=(LL) num[a[R]]*num[a[R]];            --R;        }        while(L<node[i].L){            //L++;           // cout<<L<<"---3----"<<R<<endl;            temp-=(LL) num[a[L]]*num[a[L]];            num[a[L]]--;            temp+=(LL) num[a[L]]*num[a[L]];            L++;        }        while(L>node[i].L){            L--;            //cout<<L<<"---444--"<<R<<endl;            temp-=(LL) num[a[L]]*num[a[L]];            num[a[L]]++;            temp+=(LL) num[a[L]]*num[a[L]];        }        ans[node[i].id].a=temp-(R-L+1);        ans[node[i].id].b=(LL) (R-L+1)*(R-L);        ans[node[i].id].reduce();    }}int main(){    while(~scanf("%d %d",&n,&m)){        for(int i=1;i<=n;i++){            scanf("%d",&a[i]);        }        for(int i=0;i<m;i++){            node[i].id=i;            scanf("%d%d",&node[i].L,&node[i].R);        }        unit=(int)sqrt(n);        sort(node,node+m,cmp);        for(int i=0;i<m;i++){           // cout<<node[i].L<<" "<<node[i].R<<endl;        }        work();        for(int i=0;i<m;i++){            printf("%lld/%lld\n",ans[i].a,ans[i].b);        }    }    return 0;}

Sequence

csu 1515

(求区间内abs(a[i]-a[j])==1的个数,思路:num[arr[i]]记录arr[i]的个数,要使得abs(arr[i]-x==1),那么x=arr[i]-1||x=a[rr]+1,所以,temp+=num[arr[i]+1]||temp+=num[arr[i]+1],

但是,问题来了,arr[]数组是32位整数,数组最大只能 开到arr[10^8],那么现在就只能使用map了,将arr[]数组按照从小到大的顺序map,意思是如果arr[i]>arr[j]那么map[arr[i]]>map[arr[j]],map的 值是从1到n而n最大为10^4,绰绰有余了,到此,此题再加上莫队算法就能解决了。)

Give you a sequence consisted of n numbers. You are required to answer how many pairs of numbers (ai, aj) satisfy that | ai - aj | = 1 and L ≤ i < j ≤ R.

Input

The input consists of one or more test cases.
For each case, the first line contains two numbers n and m, which represent the length of the number sequence and the number of queries. ( 1 ≤ n ≤ 10^4, 1 ≤ m ≤ 10^5 )
The second line consists of n numbers separated by n - 1 spaces.( 0 ≤ ai < 2^31 )
Then the m lines followed each contain two values Li and Ri.

Output

For each case just output the answers in m lines.

Sample Input
10 105 5 1 3 6 3 5 7 1 73 46 88 92 85 76 71 93 103 105 6
Sample Output
0003104330
#include <iostream>#include <stdio.h>#include <string.h>#include <math.h>#define siz 10005#include <algorithm>#include <map>using namespace std;struct Query{    int L,R,id;}node[siz*10];int ans[siz*10];int arr[siz],num[siz],unit,trr[siz],Np;map<int,int>Mp;int n,m;void _Init(){    Mp.clear();    for(int i=1;i<=n;i++){        trr[i]=arr[i];    }    sort(trr+1,trr+1+n);    Np=unique(trr+1,trr+1+n)-trr-1;    for(int i=1;i<=Np;i++){        Mp[trr[i]]=i;       // cout<<Mp[trr[i]]<<"&&&"<<trr[i]<<endl;    }}bool cmp(Query a,Query b){    if(a.L/unit!=b.L/unit) return a.L/unit<b.L/unit;    return a.R<b.R;}void  solve(){    int temp=0;    memset(num,0,sizeof(num));    int L=1,R=0;    int ant=-1;    for(int i=0;i<m;i++){        while(R<node[i].R){            R++;            if(trr[Mp[arr[R]]]-trr[Mp[arr[R]]-1]==1){                temp-=num[Mp[arr[R]]]*num[Mp[arr[R]]-1];            }            if(trr[Mp[arr[R]]+1]-trr[Mp[arr[R]]]==1){                temp-=num[Mp[arr[R]]]*num[Mp[arr[R]]+1];            }            num[Mp[arr[R]]]++;            if(trr[Mp[arr[R]]]-trr[Mp[arr[R]]-1]==1){                temp+=num[Mp[arr[R]]]*num[Mp[arr[R]]-1];            }            if(trr[Mp[arr[R]]+1]-trr[Mp[arr[R]]]==1){                temp+=num[Mp[arr[R]]]*num[Mp[arr[R]]+1];            }        }        while(R>node[i].R){            if(trr[Mp[arr[R]]]-trr[Mp[arr[R]]-1]==1){                temp-=num[Mp[arr[R]]]*num[Mp[arr[R]]-1];            }            if(trr[Mp[arr[R]]+1]-trr[Mp[arr[R]]]==1){                temp-=num[Mp[arr[R]]]*num[Mp[arr[R]]+1];            }            num[Mp[arr[R]]]--;            if(trr[Mp[arr[R]]]-trr[Mp[arr[R]]-1]==1){                temp+=num[Mp[arr[R]]]*num[Mp[arr[R]]-1];            }            if(trr[Mp[arr[R]]+1]-trr[Mp[arr[R]]]==1){                temp+=num[Mp[arr[R]]]*num[Mp[arr[R]]+1];            }            R--;        }        while(L<node[i].L){            if(trr[Mp[arr[L]]+1]-trr[Mp[arr[L]]]==1){                temp-=num[Mp[arr[L]]+1]*num[Mp[arr[L]]];            }            if(trr[Mp[arr[L]]]-trr[Mp[arr[L]]-1]==1){                temp-=num[Mp[arr[L]]]*num[Mp[arr[L]]-1];            }            num[Mp[arr[L]]]--;            if(trr[Mp[arr[L]]+1]-trr[Mp[arr[L]]]==1){                temp+=num[Mp[arr[L]]+1]*num[Mp[arr[L]]];            }            if(trr[Mp[arr[L]]]-trr[Mp[arr[L]]-1]==1){                temp+=num[Mp[arr[L]]]*num[Mp[arr[L]]-1];            }            L++;        }        while(L>node[i].L){            L--;            if(trr[Mp[arr[L]]+1]-trr[Mp[arr[L]]]==1){                temp-=num[Mp[arr[L]]+1]*num[Mp[arr[L]]];            }            if(trr[Mp[arr[L]]]-trr[Mp[arr[L]]-1]==1){                temp-=num[Mp[arr[L]]]*num[Mp[arr[L]]-1];            }            num[Mp[arr[L]]]++;            if(trr[Mp[arr[L]]+1]-trr[Mp[arr[L]]]==1){                temp+=num[Mp[arr[L]]+1]*num[Mp[arr[L]]];            }            if(trr[Mp[arr[L]]]-trr[Mp[arr[L]]-1]==1){                temp+=num[Mp[arr[L]]]*num[Mp[arr[L]]-1];            }        }        ans[node[i].id]=temp;    }    for(int i=0;i<m;i++){        printf("%d\n",ans[i]);    }}int main(){    while(~scanf("%d%d",&n,&m)){        for(int i=1;i<=n;i++){            scanf("%d",&arr[i]);        }        _Init();        unit=(int)sqrt(n);        for(int i=0;i<m;i++){            node[i].id=i;            scanf("%d%d",&node[i].L,&node[i].R);        }        sort(node,node+m,cmp);        solve();    }    return 0;}

原创粉丝点击