分块与莫队

分块

定义

高效点的暴力,适合求解m == n == 1e5的问题, 或者m根号n ≈ 1e7的问题,空间约3n

线段树是1e6,空间9n

时间复杂度O(m根号n),主要处理区间问题

代码

1
2
3
4
5
6
7
8
9
10
11
int block = sqrt(n); //块的大小
int t = n/block; //块的数量
if(n % block != 0) t++;
for(int i = 1; i <= t; i++){
st[i] = (i - 1) * block + 1; //各块起点
ed[i] = i*block; //各块终点
}
ed[t] = n; //最后一块终点调整一下
for(int i = 1; i <= n; i++){
pos[i] = (i-1)/block + 1; //各点位于的块
}

区间修改,区间求和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
void init(){
for(int i = 1; i <= t; i++){
for(int j = st[i]; j <= ed[i]; j++){
sum[i] += a[j]; //sum第i块区间和
}
}
}
int add[t+1] //add[i]第i块的增量标记
//处理碎片改sum,处理整块改add
void change(int l, int r, int d){
int p = pos[l], q = pos[r];
if(p == q){ //同一块,即碎片情况
for(int i = l; i <= r; i++){
a[i] += d;
}
sum[p] += d * (r - l + 1);
}else{ //多块,整块情况
for(int i = p + 1; i <= q - 1; i++){ //整块
add[i] += d;
}
for(int i = l; i <= ed[p]; i++){ //第一碎片
a[i] += d;
}
sum[p] += d * (ed[p] - l + 1);
for(int i = st[q]; i <= r; i++){ //最后碎片
a[i] += d;
}
sum[q] += d * (r - st[q] + 1);
}
}

long long ask(int l, int r){
int p = pos[l], r = pos[r];
long long ans = 0;
if(p == q){
for(int i = l; i <= r; i++){
ans += a[i];
}
ans += add[p] * (r - l + 1);
}else{
for(int i = p + 1; i <= q - 1; i++){
ans += sum[i] + add[i] * (ed[i] - st[i] + 1);
}
for(int i = l; i <= ed[p]; i++){
ans += a[i];
}
ans += add[p] * (ed[p] - l + 1);
for(int i = st[q]; i <= r; i++){
ans += a[i];
}
ans += add[q] * (r - st[q] + 1);
}
return ans;
}

区间第k大

查询区间有多少个数大于等于c,即查询c是第几大

核心思想是新建一个int b[N], b[i]维护第i块的内容, 并且每个b[i]单独有序

当查询的时候,如果块,直接对b[i]二分,如果碎片,遍历得到

修改的时候,如果块,维护add就行,b不用排序. 如果碎片,操作a[i],重新拷贝到b[i]上,重新对b[i]排序

基础莫队

莫队 = 分块 + 暴力 +离线.时间复杂度O(m根号n)

https://www.luogu.com.cn/problem/P1972(这题数据量莫队过不了,骗分而已)

某段区间有多少个不同的数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
const int N = 1e6 + 1;
int a[N], pos[N];
struct node{
int l, r, k;
}q[N];

bool cmp(node x, node y){
if(pos[x.l] != pos[y.l]){ //起点不同块
return pos[x.l] < pos[y.l];
}
if(pos[x.l] & 1){ //奇偶性排序
return x.r > y.r;
}
return x.r < y.r;
}

unordered_map<int, int>mp;
int num = 0;
int ans[N];
void add(int x){
mp[a[x]]++;
if(mp[a[x]] == 1) num++;
}
void reduce(int x){
mp[a[x]]--;
if(mp[a[x]] == 0) num--;
}

int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n;
cin>>n;
int block = sqrt(n);
for(int i = 1 ; i<= n; i++){
cin>>a[i];
pos[i] = (i-1)/block + 1;
}
int m;
cin>>m;
for(int i = 1; i <= m; i++){
cin>>q[i].l>>q[i].r;
q[i].k = i;
}
sort(q + 1, q + 1 + m, cmp);
int L = 1, R = 0;
for(int i = 1; i <= m; i++){
//缩小区间的情况
while(L < q[i].l) reduce(L++);
while(R > q[i].r) reduce(R--);
//增大时
while(L > q[i].l) add(--L);
while(R < q[i].r) add(++R);
ans[q[i].k] = num;
}
for(int i = 1; i <= m; i++){
cout<<ans[i]<<endl;
}
return 0;
}
  • Copyrights © 2023-2024 Kano
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信