莫队算法
2016-01-27 14:54
363 查看
莫队算法:离线的询问多个区间。离线,可以理解为没有修改。首先对询问的区间进行排序,排序时先以左端点按块排序及l/n,n就是左右区间的大小开根号。再按有段点排序。四个while循环给你一小段区间,左端点不动的条件下,左右移动右端点。同样,右端点不动,左右移动左端点。具体处理方法由题目决定。
例1:http://www.lydsy.com/JudgeOnline/problem.php?id=2038
小z的袜子
给N个袜子,编号代表不同的颜色。从L到R闭区间任意抽取两只,颜色一样的概率
分母显然实C(2,R-L+1).分子用莫队算法求和计算。
为防超范围还是用long long
例题1:http://codeforces.com/contest/617/problem/E
E. XOR and Favorite Number
题意:第一行n,m,k.数组的大小n,m次询问一个区间l,r内 l ≤ i ≤ j ≤ r 并且异或
ai, ai + 1, ..., aj 等于 k.
此处用到异或的特点:x^x^y
=y
先进行预处理求出每个点i,从1到i的异或值存为sum[i] 。这样sum[l-1]^sum[r]表示从a[l]异或到a[r].由此可知当sum[i]^k就能算出一个数。这个数……sum[i]
== k.ans加上该数在之前出现的次数。每一次加,其实加的是区间的个数。此题要注意加值的时候先加ans,再加cnt,减的时候相反
看了n多种代码了,考虑了好几天。。。。。
codeforce题目很多都卡边界,一般都要开到long long.懒得想就都开long long了。
失败的莫队算法
http://codeforces.com/contest/622/problem/C
询问m次,给出左端点和右端点,和x,求在区间内和x不等的位置
例1:http://www.lydsy.com/JudgeOnline/problem.php?id=2038
小z的袜子
给N个袜子,编号代表不同的颜色。从L到R闭区间任意抽取两只,颜色一样的概率
分母显然实C(2,R-L+1).分子用莫队算法求和计算。
为防超范围还是用long long
<span style="font-size:14px;">#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> using namespace std; int n,m; int a[50005]; long long cnt[50005]; long long ans[50005]; struct T { int l,r,id; bool operator < (const T a)const { if(a.l/222 == l/222) return r < a.r; return l < a.l; } }t[50005],h[50005]; long long gcd(long long a,long long b) { if(b == 0) return a; else return gcd(b,a % b); } long long fun(long long x) { return x * ( x - 1 ) / 2; } int main() { //cout<<sqrt(50005)<<endl; //freopen("in.txt","r",stdin); scanf("%d%d",&n,&m); for(int i = 1; i <= n; i ++) { scanf("%d",&a[i]); } for(int i = 0; i < m; i ++) { scanf("%d%d",&t[i].l,&t[i].r); h[i].l = t[i].l; h[i].r = t[i].r; t[i].id = i; } sort(t, t + m); int R = 1,L = 1; cnt[a[1]] ++; long long ant = 0; for(int i = 0; i < m; i ++) { while(R < t[i].r) { R ++; ant -= fun(cnt[a[R]]); cnt[a[R]] ++; ant += fun(cnt[a[R]]); } while(R > t[i].r) { ant -= fun(cnt[a[R]]); cnt[a[R]] --; ant += fun(cnt[a[R]]); R --; } //cout<<ant<<endl; while(L > t[i].l) { L --; ant -= fun(cnt[a[L]]); cnt[a[L]] ++; ant += fun(cnt[a[L]]); } while(L < t[i].l) { ant -= fun(cnt[a[L]]); cnt[a[L]] --; ant += fun(cnt[a[L]]); L ++; } ans[t[i].id] = ant; //cout<<ant<<endl; } long long fenmu; long long temp; long long yue; for(int i = 0; i < m; i ++) { temp = h[i].r - h[i].l + 1; fenmu = fun(temp); yue = gcd(fenmu,ans[i]); printf("%lld/%lld\n",ans[i]/yue,fenmu/yue); } return 0; }</span><span style="font-size: 24px;"> </span>
例题1:http://codeforces.com/contest/617/problem/E
E. XOR and Favorite Number
题意:第一行n,m,k.数组的大小n,m次询问一个区间l,r内 l ≤ i ≤ j ≤ r 并且异或
ai, ai + 1, ..., aj 等于 k.
此处用到异或的特点:x^x^y
=y
先进行预处理求出每个点i,从1到i的异或值存为sum[i] 。这样sum[l-1]^sum[r]表示从a[l]异或到a[r].由此可知当sum[i]^k就能算出一个数。这个数……sum[i]
== k.ans加上该数在之前出现的次数。每一次加,其实加的是区间的个数。此题要注意加值的时候先加ans,再加cnt,减的时候相反
看了n多种代码了,考虑了好几天。。。。。
codeforce题目很多都卡边界,一般都要开到long long.懒得想就都开long long了。
<span style="font-size:14px;">//莫队算法。四个while。先根据l排序,再根据right排序。为了避免超时,按块排序。及按l/400排序 //莫队算法需要注意的是增加的时候,先加ans,再加cnt。减的时候,先减cnt,再减ant #include <iostream> #include <cstdio> #include <algorithm> using namespace std; int n,m,temp; long long k; long long sum[1100000]; long long cnt[1100000] = {0}; long long a[1100000]; struct T { int L,R,id; bool operator < (const T a)const { if(L/400 == a.L/400) { return R < a.R; } return L < a.L; } }t[100005]; int main() { //freopen("in.txt","r",stdin); scanf("%d%d%I64d",&n,&m,&k); for(int i = 1; i <= n; i ++) { scanf("%I64d",&sum[i]); if(i != 1) sum[i] = sum[i-1] ^ sum[i]; } for(int i = 0; i < m; i++) { scanf("%d%d",&temp,&t[i].R); t[i].L = temp - 1; t[i].id = i; } sort(t,t+m); int R = 0,L = 0; cnt[0] = 1; long long ans = 0; for(int i = 0; i < m; i ++) { while(R < t[i].R) { R ++; ans += cnt[sum[R] ^ k]; cnt[sum[R]] ++; } while(R > t[i].R) { cnt[sum[R]] --; ans -= cnt[sum[R] ^ k]; R --; } // cout<<ans<<endl; while(L > t[i].L) { L --; ans += cnt[sum[L] ^ k]; cnt[sum[L]] ++; } while(L < t[i].L) { cnt[sum[L]] --;//先把cnt减了 ans -= cnt[sum[L] ^ k]; L ++; } a[t[i].id] = ans; } for(int i = 0; i < m; i ++) { cout<<a[i]<<endl; } return 0; }</span>
失败的莫队算法
http://codeforces.com/contest/622/problem/C
C. Not Equal on a Segment(思维)
给一个数组询问m次,给出左端点和右端点,和x,求在区间内和x不等的位置
#include <iostream> #include <cstdio> #include <algorithm> #include <list> #include <map> #include <stack> #include <vector> #include <cstring> #include <sstream> #include <string> #include <set> using namespace std; int n,m; int a[200004]; set<int>k; struct t { int l,r,x,id; bool operator < (const t a)const { if(a.l / 1000 == l / 1000) { return a.r > r; } else return a.l/1000 > l / 1000; } } s[200004]; int ans[200004]; int cnt[1000002] = {0}; int num[1000004]; int main() { //freopen("in.txt","r",stdin); scanf("%d%d",&n,&m); for(int i = 1; i <= n; i ++) { scanf("%d",&a[i]); } for(int i = 0; i < m; i ++) { scanf("%d%d%d",&s[i].l,&s[i].r,&s[i].x); s[i].id = i; } sort(s,s + m); k.clear(); int l = 1,r = 1; cnt[a[1]] ++; num[a[1]] = 1; k.insert(a[1]); for(int i = 0; i < m; i ++) { while(s[i].r > r) { r ++; if(r >= s[i].l && r <= s[i].r) num[a[r]] = r; if(cnt[a[r]] == 0) { k.insert(a[r]); } cnt[a[r]] ++; } while(s[i].r < r) { if(cnt[a[r]] == 1) { num[a[r]] = -1; k.erase(a[r]); } cnt[a[r]] --; r --; if(r >= s[i].l && r <= s[i].r) num[a[r]] = r; } while(s[i].l < l) { l --; if(l >= s[i].l && l <= s[i].r) num[a[l]] = l; if(cnt[a[l]] == 0) { k.insert(a[l]); } cnt[a[l]] ++; } while(s[i].l > l) { if(cnt[a[l] ]== 1) { k.erase(a[l]); num[a[l]] = -1; } cnt[a[l]] --; l ++; if(l > 44) cout<<endl; if(l >= s[i].l && l <= s[i].r) num[a[l]] = l; } for(set<int>::iterator it = k.begin(); it != k.end(); it ++) { //printf("num[%d] = %d\n",*it,num[*it]); if(*it != s[i].x) { if(num[*it] >= s[i].l && num[*it] <= s[i].r) ans[s[i].id] = num[*it]; else//对于苦逼情况的处理 { for(int t = s[i].l ; t <= s[i].r; t ++) { if(a[t] != s[i].x) { ans[s[i].id] = t; } } } } } } for(int i =0; i < m; i ++) { if(ans[i] == 0) ans[i] = -1; printf("%d\n",ans[i]); } return 0; } /*8 2 2 1 2 2 1 2 2 2 2 7 2 4 8 2*/正确做法如下
//last[i],表示i位置上和它数值不同,且在它之前的最大位置 //如果last[r] != x.r就是那个位置 //如果last[r] == x.那就要往前找离他最近的不同的位置,,那个位置就是与x不同的位置。 //最右侧,最靠右的地方 #include <iostream> #include <cstdio> using namespace std; int a[1000005]; int last[1000005]; int main() { // freopen("in.txt","r",stdin); int m,n,l,r,x; scanf("%d%d",&n,&m); for(int i = 1; i <= n; i ++) { scanf("%d",&a[i]); } last[1] = -1; for(int i = 2; i <= n; i ++) { if(a[i] == a[i - 1]) last[i] = last[i - 1]; else last[i] = i -1; } for(int i = 0; i < m; i ++) { scanf("%d%d%d",&l,&r,&x); if(a[r] == x) { if(last[r] < l) { printf("-1\n"); } else printf("%d\n",last[r]); } else { printf("%d\n",r); } } return 0; }
相关文章推荐
- VS2015快捷键
- 机器学习算法原理与实践(六)、感知机算法
- Good Bye 2015 D. New Year and Ancient Prophecy(dp+LCP)
- javaKMP算法
- Java递归算法
- SpringMVC基础-HelloWorld
- shell 脚本 测试webApp
- 验证思路(转)
- Java8新特性
- java tomcat 远程调试 在服务器上debug
- ActiveMQ 主备
- 快速理解聚集索引和非聚集索引
- 星星之火可以燎原
- poj2411 Mondriaan's Dream 状压dp
- 关于Eclipse项目中js文件报错(missing semicolon)
- git 如何让单个文件回退到指定的版本(转)
- 【Linux】初识Linux
- 算法之旅——归并排序
- JAVA随机数之多种方法从给定范围内随机N个不重复数
- 20160127_Android程序完全退出的方法