普通莫队
形式
对于n个数的序列,我们要对m个区间进行询问,当我们已知 [ l , r ] [l,r] [l,r]可以在 O ( 1 ) O(1) O(1)的复杂度求出 [ l , r − 1 ] , [ l , r + 1 ] , [ 1 − 1 , r ] , [ l + 1 , r ] [l,r-1],[l,r+1],[1-1,r],[l+1,r] [l,r−1],[l,r+1],[1−1,r],[l+1,r],我们可以用莫队算法求解复杂度 O ( n n ) O(n\sqrt n) O(nn)。
主要用在对多个区间进行询问的离线算法(可以和很多算法结合起来)。
复杂度分析:见 o i − w i k i oi-wiki oi−wiki
普通版本
我们将要查询的序列分组,然后对查询的区间 [ l , r ] [l,r] [l,r]进行排序:排序规则为先按 l l l所在分组的编号进行排序,如果在同一组就按 r r r的大小进行排序。
bool cmp(Y x, Y y) {
if(x.l/block != y.l/block) return x.l < y.l;
else return x.r < y.r;
}
奇偶化优化版本
先按 l l l所在的块进行排序,如果在同一块中,假如这个块是奇数块就按从小到大进行排序,偶数块就按从大到小进行排序,快 30 % 30\% 30%左右。
bool cmp1(Y x, Y y) {
if(x.l/block != y.l/block) return x.l < y.l;
if((x.l/block) & 1) return x.r < y.r;
return x.r > y.r;
}
题目
P2709小B的询问
公式推导:
∑
i
=
1
k
c
i
2
当
其
中
项
变
成
(
c
i
+
1
)
2
=
c
i
2
+
2
c
i
+
1
贡
献
加
了
2
c
i
+
1
。
\sum_{i=1}^kc_i^2\\ 当其中项变成(c_i+1)^2=c_i^2+2c_i+1\\ 贡献加了2c_i+1。
i=1∑kci2当其中项变成(ci+1)2=ci2+2ci+1贡献加了2ci+1。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e4+10;
int s[N], block, cnt[N], A[N];
struct Y {
int l, r, i;
Y (int x=0, int y=0, int z=0) : l(x), r(y), i(z){}
} a[N];
//普通分块
bool cmp(Y x, Y y) {
if(x.l/block != y.l/block) return x.l < y.l;
else return x.r < y.r;
}
bool cmp1(Y x, Y y) {
if(x.l/block != y.l/block) return x.l < y.l;
if((x.l/block) & 1) return x.r < y.r;
return x.r > y.r;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int n, m, k, x, y;
cin >> n >> m >> k;
block = ceil(sqrt(n));
for(int i=1; i<=n; i++) {
cin >> s[i];
}
for(int i=1; i<=m; i++) {
cin >> x >> y;
a[i] = Y(x, y, i);
}
sort(a+1, a+1+m, cmp);
int l=1, r=0, ans=0;
for(int i=1; i<=m; i++) {
while(l > a[i].l) l--, cnt[s[l]]++, ans += 2*cnt[s[l]]-1;
while(r < a[i].r) r++, cnt[s[r]]++, ans += 2*cnt[s[r]]-1;
while(l < a[i].l) cnt[s[l]]--, ans -= 2*cnt[s[l]]+1, l++;
while(r > a[i].r) cnt[s[r]]--, ans -= 2*cnt[s[r]]+1, r--;
A[a[i].i] = ans;
}
for(int i=1; i<=m; i++) cout << A[i] << endl;
return 0;
}
P1494[国家集训队]小Z的袜子(维护cnt来求概论)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e4+10;
ll s[N], u[N], v[N], cnt[N], block;
struct Y {
ll l, r, i;
Y(int x=0, int y=0, int z=0):l(x), r(y), i(z) {}
} a[N];
//对于不同块的区间,按所在块的顺序进行排序
//对于同一块的区间,按右端点的值进行排序
//普通分块
bool cmp(Y a, Y b) {
if(a.l/block != b.l/block) return a.l < b.l;
else return a.r < b.r;
}
//奇偶化分块
bool cmp1(Y a, Y b) {
if(a.l/block != b.l/block) return a.l < b.l;
if((a.l/block) & 1) return a.r < b.r;
return a.r > b.r;
}
ll cal(ll x) {
return x*(x-1)/2;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
ll n, m, x, y, z;
cin >> n >> m;
block = ceil(sqrt(n));
for(int i=1; i<=n; i++) {
cin >> s[i];
}
for(int i=1; i<=m; i++) {
cin >> x >> y;
a[i] = Y(x, y, i);
}
sort(a+1, a+1+m, cmp);
ll l = 1, r = 0, ans = 0;
for(int i=1; i<=m; i++) {
while(l > a[i].l) {
l--, cnt[s[l]]++;
ans = ans - cal(cnt[s[l]]-1) + cal(cnt[s[l]]);
}
while(r < a[i].r) {
r++, cnt[s[r]]++;
ans = ans - cal(cnt[s[r]]-1) + cal(cnt[s[r]]);
}
while(l < a[i].l) {
cnt[s[l]]--;
ans = ans - cal(cnt[s[l]]+1) + cal(cnt[s[l]]);
l++;
}
while(r > a[i].r) {
cnt[s[r]]--;
ans = ans - cal(cnt[s[r]]+1) + cal(cnt[s[r]]);
r--;
}
u[a[i].i] = ans, v[a[i].i] = cal(a[i].r-a[i].l+1);
}
for(int i=1; i<=m; i++) {
if(u[i] == 0) cout << "0/1\n";
else {
int k = __gcd(u[i], v[i]);
cout << u[i]/k << '/' << v[i]/k << endl;
}
}
return 0;
}
P3709大爷的字符串题
用 c n t cnt cnt和 t t t来维护区间最多出现的元素出现了几次
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5+10;
int block, ans = 0;
int s[N], cnt[N], t[N], res[N];
struct Y {
ll l, r, i;
Y(int x=0, int y=0, int z=0):l(x), r(y), i(z) {}
} a[N];
struct X{
int id, x;
} b[N];
bool c(X a, X b) {
return a.x < b.x;
}
//奇偶化分块
bool cmp1(Y a, Y b) {
if(a.l/block != b.l/block) return a.l < b.l;
if((a.l/block) & 1) return a.r < b.r;
return a.r > b.r;
}
void add(int x) {
t[cnt[x]]--;
cnt[x]++;
t[cnt[x]]++;
ans = max(cnt[x], ans);
}
void del(int x) {
t[cnt[x]]--;
if(t[cnt[x]] == 0 && ans == cnt[x]) ans--;
cnt[x]--;
t[cnt[x]]++;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int n, m, x, y, t = 1;
scanf("%d%d", &n, &m);
block = ceil(sqrt(n));
for(int i=1; i<=n; i++) {
cin >> b[i].x;
b[i].id = i;
}
sort(b+1, b+1+n, c);
s[b[1].id] = 1;
for(int i=2; i<=n; i++) {
if(b[i].x != b[i-1].x) t++;
s[b[i].id] = t;
}
// for(int i=1; i<=n; i++) cout << s[i] << endl;
for(int i=1; i<=m; i++) {
scanf("%d%d", &x, &y);
a[i] = Y(x, y, i);
}
int l = 1, r = 0;
sort(a+1, a+1+m, cmp1);
for(int i=1; i<=m; i++) {
while(l > a[i].l) l--, add(s[l]);
while(r < a[i].r) r++, add(s[r]);
while(l < a[i].l) del(s[l]), l++;
while(r > a[i].r) del(s[r]), r--;
res[a[i].i] = ans;
}
for(int i=1; i<=m; i++) printf("%d\n", -res[i]);
return 0;
}