莫队算法总结
来源:互联网 发布:sql server 官网 编辑:程序博客网 时间:2024/05/17 06:34
莫队算法主要是利用了曼哈顿距离的长度关系,将每一个询问的坐标抽象为一个点,然后将询问分块,进行排序(分块的原因是使得这些询问坐标的移动的曼哈顿距离达到 最小),排序之后再按照此时的顺序进行左右区间的移动,而在内部的实现的过程就要看各个题目的要求了,所以在这里主要是理解到,莫队算法的核心是:平面上移动曼哈顿距离最小 (利用分块求得平面上曼哈顿距离的最小生成树)+ 离线查询(无法在线查询),在解题的过程中也要想到使得每一次转移计算答案的时间也要是O(1)的,到此,莫队算法也就完成。
三个样例:
HH的项链
(询问区间内不同数的个数,temp记录答案,只要num[arr[x]]>0那么temp+=1)
M行,每行一个整数,依次表示询问对应的答案。
61 2 3 4 3 531 2 3 52 6
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)以方便自己选择。
输入文件第一行包含两个正整数N和M。N为袜子的数量,M为小Z所提的询问的数量。接下来一行包含N个正整数Ci,其中Ci表示第i只袜子的颜色,相同的颜色用相同的数字表示。再接下来M行,每行两个正整数L,R表示一个询问。
包含M行,对于每个询问在一行中输出分数A/B表示从该询问的区间[L,R]中随机抽出两只袜子颜色相同的概率。若该概率为0则输出0/1,否则输出的A/B必须为最简分数。(详见样例)
6 4
1 2 3 3 3 2
2 6
1 3
3 5
1 6
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.
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.
For each case just output the answers in m lines.
10 105 5 1 3 6 3 5 7 1 73 46 88 92 85 76 71 93 103 105 6
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;}
- 莫队算法总结
- 莫队算法总结
- (翻译+部分总结)莫队算法入门小结
- 算法总结
- 算法总结
- 算法总结
- 算法总结
- 算法总结
- 算法总结
- 算法总结
- 算法总结
- 算法总结
- 算法总结
- 算法总结
- 算法总结
- 算法总结
- 算法总结
- 算法总结
- stick
- idea mybatis maven generate命令
- Java_基础—图片加密
- Android 自动获取输入短信验证码库AutoVerifyCode
- java高并发解决方案
- 莫队算法总结
- 策略模式
- 前端跨域请求原理及实践
- hdu 5246 超级赛亚ACMer【二分or模拟】
- iOS ijkplayer视频直播框架
- mysql生成日历脚本(年|年月 格式)
- Android零基础入门第13节:Android Studio配置优化,打造开发利器
- Java 5
- Android 6.0 变更