莫队算法入门(暴力而不失优雅)
初学者建议观看:
这里有一个关于块大小的优化和奇偶性优化
块大小优化
好吧,在写这个之前,我从机房巨佬空中得到了一个结论莫队的复杂度是
(S为块大小)
但实际上是
证明略
故我们可以适当的调大块的大小
由爆OJ得,本题块大小应当在左右(不适用所有程序)
奇偶性优化
若上一块中的右端点坐标是递增的,则这块中右端点递减
若上一块中的右端点坐标是递减的,则这块中右端点递增
这样的话,原本在块转移时右端点的移动情况由
变为
故变得更优
例如:
例如样例
1 2
1 100
11 100
11 20
若使用奇偶性优化
1,2——>1,100——>11,100——>11,20
不使用
1,2——>1,100——>11,20——>11,100
可以看出还是有很大的优化的
int cmp(query a, query b) { return (belong[a.l] ^ belong[b.l]) ? belong[a.l] < belong[b.l] : ((belong[a.l] & 1) ? a.r < b.r : a.r > b.r); }
板子题目:这个是弱化版的
莫队的优化2:大佬说的会快一点
Luogu P1972 [SDOI2009]HH的项链
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include<iostream> using namespace std; template <typename Tp> void read(Tp &x){//read(n); x=0;char ch=1;int fh; while(ch!='-'&&(ch>'9'||ch<'0')){ ch=getchar(); } if(ch=='-'){ fh=-1;ch=getchar(); }else fh=1; while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+ch-'0';ch=getchar(); } x*=fh; } inline void write(int x){if(x>9) write(x/10);putchar(x%10+48);return;} const int maxn=1e6+100; int n; int block,tot; int a[maxn],res[maxn]; int belong[maxn]; int cnt[maxn]; struct node{ int l,r; int id; bool operator<(const node&x) const{return belong[l]^belong[x.l]?l<x.l:belong[x.l]&1?r<x.r:r>x.r;} }q[maxn]; int ans=0; int m; //bool cmp(node x,node y){ // if(belong[x.l]!=belong[y.l]){ // return belong[x.l]<belong[y.l]; // } // else{ // if(belong[x.l]&1){ // return x.r<y.r; // } // else{ // return x.r>y.r; // } // } //} void add(int pos){ if(++cnt[a[pos]]==1){ ans++; } return ; } void remove(int pos){ if(--cnt[a[pos]]==0){ ans--; } return ; } int main(){ read(n); block=sqrt(n); for(register int i=1;i<=n;++i){ read(a[i]); belong[i]=(i-1)/block+1; } read(m); for(register int i=1;i<=m;++i){ read(q[i].l); read(q[i].r); q[i].id=i; } sort(q+1,q+m+1); int ql,qr; for(register int i=1,l=1,r=0;i<=m;++i){ ql=q[i].l,qr=q[i].r; while(l<ql){ remove(l++); } while(l>ql){ add(--l); } while(r>qr){ remove(r--); } while(r<qr){ add(++r); } res[q[i].id]=ans; } for(register int i=1;i<=m;++i){ //printf("%d\n",res[i]); write(res[i]); putchar(10); } }
例题二:
第一行三个整数 n,m,kn,m,k。
第二行 nn 个整数,表示 小B 的序列。
接下来的 mm 行,每行两个整数 l,rl,r。
输出格式
输出 mm 行,每行一个整数,对应一个询问的答案。
输入输出样例
输入 #1
6 4 3 1 3 2 1 1 3 1 4 2 6 3 5 5 6
输出 #1
6 9 5 2
说明/提示
【数据范围】
对于 100% 的数据,1≤n,m,k≤5×104。
#include<iostream> #include<algorithm> #include<math.h> typedef long long ll; using namespace std; const int maxn=1e6+100; int belong[maxn],a[maxn]; int block; int n,m,k; int l=1,r=0; int cnt[maxn]; ll ans=0; struct node{ int l,r; int id; }q[maxn]; ll res[maxn]; bool cmp(node x,node y){ if(belong[x.l]^belong[y.l]){ return x.l<y.l; } else{ if(belong[x.l]&1){ return x.r<y.r; } else{ return x.r>y.r; } } } void del(int pos){ ans-=(cnt[a[pos]]*cnt[a[pos]]);//就是先减去在加上 --cnt[a[pos]]; ans+=(cnt[a[pos]]*cnt[a[pos]]); } void add(int pos){ ans-=(cnt[a[pos]]*cnt[a[pos]]); ++cnt[a[pos]]; ans+=(cnt[a[pos]]*cnt[a[pos]]); } int main(){ scanf("%d%d%d",&n,&m,&k); block=sqrt(n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); belong[i]=(i-1)/block+1; } for(int i=1;i<=m;i++){ scanf("%d%d",&q[i].l,&q[i].r); q[i].id=i; } sort(q+1,q+m+1,cmp); // ql qr for(int i=1;i<=m;i++){ int ql=q[i].l,qr=q[i].r; while(l<ql){ del(l++);//删除的这个也要删除 } while(l>ql){ add(--l); } while(r>qr){ del(r--); } while(r<qr){ add(++r); } res[q[i].id]=ans; } for(int i=1;i<=m;i++){ printf("%d\n",res[i]); } }
求区间里任取两个数是相同的概率
#include<iostream> #include<algorithm> #include<math.h> template <typename Tp> void read(Tp &x){//read(n); x=0;char ch=1;int fh; while(ch!='-'&&(ch>'9'||ch<'0')){ ch=getchar(); } if(ch=='-'){ fh=-1;ch=getchar(); }else fh=1; while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+ch-'0';ch=getchar(); } x*=fh; } typedef long long ll; using namespace std; const int maxn=1e6+100; ll gcd(ll a,ll b){ if(b==0){ return a; } return gcd(b,a%b); } struct node{ ll l,r; int id; }q[maxn]; ll block,n,m; ll belong[maxn],cnt[maxn],a[maxn]; ll l=1,r=0,ans=0; bool cmp(node x,node y){ if(belong[x.l]^belong[y.l]){ return x.l<y.l; } else{ if(belong[x.l]&1){ return x.r<y.r; } else{ return x.r>y.r; } } } void add(int pos){ ans-=(ll)((cnt[a[pos]])*(cnt[a[pos]]-1)/2); ++cnt[a[pos]]; ans+=(ll)((cnt[a[pos]])*(cnt[a[pos]]-1)/2); } void del(int pos){ ans-=(ll)((cnt[a[pos]])*(cnt[a[pos]]-1)/2); --cnt[a[pos]]; ans+=(ll)((cnt[a[pos]])*(cnt[a[pos]]-1)/2); } bool cmp1(node x,node y){ return x.id<y.id; } int main(){ read(n),read(m); // block=sqrt(n); block=n/sqrt(m*2/3);//可能会快一点 for(register int i=1;i<=n;i++){ read(a[i]); belong[i]=(i-1)/block+1; } for(register int i=1;i<=m;i++){ read(q[i].l),read(q[i].r); q[i].id=i; } sort(q+1,q+m+1,cmp); // ql qr for(register int i=1;i<=m;i++){ int ql=q[i].l,qr=q[i].r; while(l<ql){ del(l++); } while(l>ql){ add(--l); } while(r>qr){ del(r--); } while(r<qr){ add(++r); } if(qr==ql){ q[i].l=0; q[i].r=1; continue; } ll p=(ll)(qr-ql+1)*(qr-ql)/2; ll d=gcd(p,ans); q[i].l=ans/d; q[i].r=p/d; } sort(q+1,q+m+1,cmp1); for(register int i=1;i<=m;i++){ printf("%lld/%lld\n",q[i].l,q[i].r); } }
例三:
输入复制
10 5
1 1 1 1 1 2 2 2 2 2
4 7 2
4 7 3
4 8 2
4 8 3
3 8 3
输出复制
0
2
1
1
0
主要是要离散化(两种离散化的方法都一样)
把数离散化之后再暴力求解,其实莫队还是维护的区间每个值的个数
#pragma GCC optimize(1) #pragma GCC optimize(2) #pragma GCC optimize(3,"Ofast","inline") #include <iostream> #include <algorithm> #include <math.h> template <typename Tp> void read(Tp &x) { //read(n); x = 0; char ch = 1; int fh; while (ch != '-' && (ch > '9' || ch < '0')) { ch = getchar(); } if (ch == '-') { fh = -1; ch = getchar(); } else fh = 1; while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + ch - '0'; ch = getchar(); } x *= fh; } typedef long long ll; using namespace std; const int maxn = 1e6 + 100; int gcd(int a, int b) { if (b == 0) { return a; } return gcd(b, a % b); } struct node { int l, r, k; int id; } q[maxn]; int block, n, m, nn, len = 0; int ql, qr, k; int belong[maxn], cnt[maxn], a[maxn], res[maxn], mp[maxn], b[maxn]; int l = 1, r = 0, ans = 0; bool cmp(node x, node y) { if (belong[x.l]^belong[y.l]) { return x.l < y.l; } else { if (belong[x.l] & 1) { return x.r < y.r; } else { return x.r > y.r; } } } void add(int pos) { ++cnt[a[pos]]; } void del(int pos) { --cnt[a[pos]]; } void inint() { sort(b + 1, b + n + 1); len = unique(b + 1, b + n + 1) - (b + 1); for (int i = 1; i <= n; i++) { a[i] = lower_bound(b + 1, b + len + 1, a[i]) - b; } // cout<<len<<endl; } int x; int getint() { read(x); if (mp[x] != 0) { return mp[x]; } else { mp[x] = ++len; return mp[x]; } } int main() { read(n), read(m); // block=sqrt(n); block = n / sqrt(m * 2 / 3); //可能会快一点 for (register int i = 1; i <= n; ++i) { read(a[i]); // a[i]=getint(); b[i] = a[i]; belong[i] = (i - 1) / block + 1; } inint(); for (register int i = 1; i <= m; ++i) { read(q[i].l), read(q[i].r), read(q[i].k); q[i].id = i; } sort(q + 1, q + m + 1, cmp); for (register int i = 1; i <= m; ++i) { ql = q[i].l, qr = q[i].r, k = q[i].k; while (l < ql) { del(l++); } while (l > ql) { add(--l); } while (r > qr) { del(r--); } while (r < qr) { add(++r); } for (register int j = 1; j <= len; ++j) { if (!cnt[j]) { continue; } if (gcd(k, cnt[j]) == 1) { ++res[q[i].id]; } } } for (register int i = 1; i <= m; ++i) { printf("%d\n", res[i]); } }