莫队算法小结
神奇的莫队算法!!!我只能说Qrz,之前一直在一道题上面傻逼了,wa到死,附上链接:http://www.spoj.com/problems/ZQUERY/en/ 个人觉得还是一道蛮不错的题。
莫队简单粗暴,据说可以再o(nlogn)内解决一切无修改的区间查询问题!!!核心应该就是:1:按左端点分块排序。2:能在o(1)的复杂度内解决[L,R] -> [L,R+1],[L,R]->[L,R-1],[L,R] -> [L-1,R],[L,R] -> [L+1,R]所维护的值,当着o(1)视情况而定。只要保证复杂度满足题目条件即可。
莫队另外一个优点就是易于实现!!!好写!!!再次Orz莫涛大神!!!
下面介绍几道入门题:
1:小Z的袜子:http://www.lydsy.com/JudgeOnline/problem.php?id=2038
最基本的题,直接给代码了:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #define FOR(i,x,y) for(int i = x;i < y;i ++) #define IFOR(i,x,y) for(int i = x;i > y;i --) #define ll long long #define N 50010 using namespace std; const int M = (int)sqrt(1.0*N)+1; int n,m; int cnt[N],col[N]; struct Command{ ll l,r,id; bool operator < (const Command& rhs) const{ if(l/M == rhs.l/M) return r < rhs.r; return (l/M) < (rhs.l/M); } }cmd[N]; struct Ans{ ll mol,den; }ans[N]; ll gcd(ll a,ll b){ return b == 0 ? a : gcd(b,a%b); } void solve(){ ll L = 1,R = 0; ll temp = 0; FOR(i,0,m){ while(R < cmd[i].r) { R ++; temp -= cnt[col[R]]*cnt[col[R]]; cnt[col[R]] ++; temp += cnt[col[R]]*cnt[col[R]]; } while(R > cmd[i].r){ temp -= cnt[col[R]]*cnt[col[R]]; cnt[col[R]] --; temp += cnt[col[R]]*cnt[col[R]]; R --; } while(L < cmd[i].l){ temp -= cnt[col[L]]*cnt[col[L]]; cnt[col[L]] --; temp += cnt[col[L]]*cnt[col[L]]; L ++; } while(L > cmd[i].l){ L --; temp -= cnt[col[L]]*cnt[col[L]]; cnt[col[L]] ++; temp += cnt[col[L]]*cnt[col[L]]; } ll x = temp - (R-L+1); ll y = (R-L+1)*(R-L); if(!x) ans[cmd[i].id].mol = 0,ans[cmd[i].id].den = 1; else{ ll tem = gcd(x,y); ans[cmd[i].id].mol = x/tem; ans[cmd[i].id].den = y/tem; } } } int main() { //freopen("test.in","r",stdin); while(~scanf("%d%d",&n,&m)){ memset(cnt,0,sizeof(cnt)); FOR(i,1,n+1) scanf("%d",&col[i]); FOR(i,0,m){ scanf("%lld%lld",&cmd[i].l,&cmd[i].r); cmd[i].id = i; } sort(cmd,cmd+m); solve(); FOR(i,0,m){ printf("%lld/%lld\n",ans[i].mol,ans[i].den); } } return 0; }2:Sona https://ac.2333.moe/Problem/view.xhtml?id=1457
这道题给人一种然并卵的感觉,和上题基本一样
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #define FOR(i,x,y) for(int i = x;i < y;i ++) #define IFOR(i,x,y) for(int i = x;i > y;i --) #define ll long long #define N 100100 using namespace std; const int M = (int)sqrt(1.0*N); int n,m; ll cnt[N],col[N]; ll ans[N]; struct Col{ ll note; int id; bool operator < (const Col& rhs) const{ return note < rhs.note; } }con[N]; struct Commend{ int l,r; int id; bool operator < (const Commend& rhs) const{ if(l/M == rhs.l/M) return r < rhs.r; return (l/M) < (rhs.l/M); } }cmd[N]; void solve(){ int L = 1,R = 0; FOR(i,0,N) cnt[i] = 0; ll temp = 0; FOR(i,0,m){ while(R < cmd[i].r){ R ++; temp -= cnt[col[R]]*cnt[col[R]]*cnt[col[R]]; cnt[col[R]] ++; temp += cnt[col[R]]*cnt[col[R]]*cnt[col[R]]; } while(R > cmd[i].r){ temp -= cnt[col[R]]*cnt[col[R]]*cnt[col[R]]; cnt[col[R]] --; temp += cnt[col[R]]*cnt[col[R]]*cnt[col[R]]; R --; } while(L < cmd[i].l){ temp -= cnt[col[L]]*cnt[col[L]]*cnt[col[L]]; cnt[col[L]] --; temp += cnt[col[L]]*cnt[col[L]]*cnt[col[L]]; L ++; } while(L > cmd[i].l){ L --; temp -= cnt[col[L]]*cnt[col[L]]*cnt[col[L]]; cnt[col[L]] ++; temp += cnt[col[L]]*cnt[col[L]]*cnt[col[L]]; } ans[cmd[i].id] = temp; } } int main() { //freopen("test.in","r",stdin); while(~scanf("%d",&n)){ FOR(i,0,n) {scanf("%I64d",&con[i].note);con[i].id = i+1;} sort(con,con+n); int cot = 0; col[con[0].id] = (++cot); FOR(i,1,n){ if(con[i].note != con[i-1].note) col[con[i].id] = (++cot); else col[con[i].id] = cot; } scanf("%d",&m); FOR(i,0,m){ scanf("%d%d",&cmd[i].l,&cmd[i].r); cmd[i].id = i; } sort(cmd,cmd+m); solve(); FOR(i,0,m){ printf("%I64d\n",ans[i]); } } return 0; }
3:Group http://acm.hdu.edu.cn/showproblem.php?pid=4638 维护一个左边数的位置,右边数的位置,1,n特判一下就行了
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define ll long long #define N 100010 #define FOR(i,x,y) for(int i = x;i < y;i ++) #define IFOR(i,x,y) for(int i = x;i > y;i --) using namespace std; const int M = (int)sqrt(1.0*N); int num[N],pos[N],ans[N],l[N],r[N]; int n,m; struct Commend{ int l,r; int id; bool operator < (const Commend& rhs) const{ if(l/M == rhs.l/M) return r < rhs.r; return (l/M < rhs.l/M); } }cmd[N]; void solve(){ int L = 1,R = 0; int temp = 0; FOR(i,0,m){ while(R < cmd[i].r){ R ++; if(l[num[R]] >= L && r[num[R]] <= R) temp --; else if((l[num[R]] < L && r[num[R]] > R) || (l[num[R]] > R) || (r[num[R]] < L)) temp ++; } while(R > cmd[i].r){ if(l[num[R]] >= L && r[num[R]] <= R) temp ++; else if((l[num[R]] < L && r[num[R]] > R) || (l[num[R]] > R) || (r[num[R]] < L)) temp --; R --; } while(L < cmd[i].l){ if(l[num[L]] >= L && r[num[L]] <= R) temp ++; else if((l[num[L]] < L && r[num[L]] > R) || (l[num[L]] > R) || (r[num[L]] < L)) temp --; L ++; } while(L > cmd[i].l){ L --; if(l[num[L]] >= L && r[num[L]] <= R) temp --; else if((l[num[L]] < L && r[num[L]] > R) || (l[num[L]] > R) || (r[num[L]] < L)) temp ++; } ans[cmd[i].id] = temp; } } int main() { //freopen("test.in","r",stdin); int t; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); FOR(i,1,n+1){ scanf("%d",&num[i]); pos[num[i]] = i; } l[1] = 0; r[1] = pos[2]; r[n] = n+1; l[n] = pos[n-1]; FOR(i,2,n){ l[i] = min(pos[i-1],pos[i+1]); r[i] = max(pos[i-1],pos[i+1]); } FOR(i,0,m){ scanf("%d%d",&cmd[i].l,&cmd[i].r); cmd[i].id = i; } sort(cmd,cmd+n); solve(); FOR(i,0,m){ printf("%d\n",ans[i]); } } return 0; }
4:Zero Query http://www.spoj.com/problems/ZQUERY/en/ 我wa到死的一题。这个题要维护一个左边最靠近的一个位置,右边靠近的一个位置,就行了
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <map> #define FOR(i,x,y) for(int i = x;i < y;i ++) #define IFOR(i,x,y) for(int i = x;i > y;i --) #define lrt rt<<1 #define rrt rt<<1|1 #define lson rt<<1,l,mid #define rson rt<<1|1,mid+1,r #define ll long long #define N 50050 using namespace std; int M; int cnt,col[N],sum[N],n,m,l[N],r[N],st,pr[N],pl[N],ans[N],pos[N]; int maxn,minn; struct Commend{ int l,r; int id; bool operator < (const Commend& rhs) const{ if(l/M == rhs.l/M) return r < rhs.r; return (l/M) < (rhs.l/M); } }cmd[N]; struct Tree{ int l,r; int maxx; }tree[N<<2]; void Build(int rt,int l,int r){ tree[rt].l = l; tree[rt].r = r; tree[rt].maxx = 0; if(l == r) return; int mid = (l+r)>>1; Build(lson); Build(rson); } void PushUp(int rt) {tree[rt].maxx = max(tree[lrt].maxx,tree[rrt].maxx);} void Modify(int rt,int k,int val){ if(tree[rt].l == tree[rt].r) {tree[rt].maxx = val;return;} int mid = (tree[rt].l + tree[rt].r)>>1; if(k <= mid) Modify(lrt,k,val); else Modify(rrt,k,val); PushUp(rt); } void solve(){ int L = 1,R = 0; memset(r,-1,sizeof(r)); memset(l,-1,sizeof(l)); FOR(i,0,m){ while(R < cmd[i].r){ R ++; if(l[col[R]] == -1) l[col[R]] = R; r[col[R]] = R; Modify(1,col[R],r[col[R]]-l[col[R]]); } while(R > cmd[i].r){ r[col[R]] = pr[R]; if(r[col[R]] < L) l[col[R]] = r[col[R]] = -1; Modify(1,col[R],r[col[R]]-l[col[R]]); R --; } while(L < cmd[i].l){ l[col[L]] = pl[L]; if(l[col[L]] > R || l[col[L]] == -1) l[col[L]] = r[col[L]] = -1; Modify(1,col[L],r[col[L]]-l[col[L]]); L ++; } while(L > cmd[i].l){ L --; if(r[col[L]] == -1) r[col[L]] = L; l[col[L]] = L; Modify(1,col[L],r[col[L]]-l[col[L]]); } ans[cmd[i].id] = tree[1].maxx; } } int main() { //freopen("test.in","r",stdin); while(~scanf("%d%d",&n,&m)){ M = (int)sqrt(1.0*n); int tem; sum[1] = col[1] = 0; maxn = minn = 0; FOR(i,2,n+2){ scanf("%d",&tem); sum[i] = sum[i-1] + tem; if(tem == 1) maxn = max(maxn,sum[i]); else minn = min(minn,sum[i]); } cnt = 1-minn; FOR(i,1,n+2){ col[i] = sum[i] + cnt; } cnt = maxn-minn+1; Build(1,1,cnt); FOR(i,0,m){ scanf("%d%d",&cmd[i].l,&cmd[i].r); cmd[i].r ++; cmd[i].id = i; } sort(cmd,cmd+m); FOR(i,1,cnt+1) pos[i] = -1; FOR(i,1,n+2){ pr[i] = pos[col[i]]; pos[col[i]] = i; } FOR(i,1,cnt+1) pos[i] = -1; IFOR(i,n+1,0){ pl[i] = pos[col[i]]; pos[col[i]] = i; } solve(); FOR(i,0,m){ printf("%d\n",ans[i]); } } return 0; }5:The sum of gcd http://acm.hdu.edu.cn/showproblem.php?pid=5381 和上一题有点像,稍微复杂一点,可以看我这道题写的题解
http://blog.csdn.net/u014610830/article/details/47670615
版权声明:本文为博主原创文章,未经博主允许不得转载。