分块
定义
定义n为数组长度,m为查询次数
优雅的暴力,适合求解m == n == 1e5的问题, 或者m根号n ≈ 1e7的问题,空间约3n
相比之下,线段树解决1e6,空间9n
时间复杂度O(m根号n),主要处理区间问题
代码
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; //各点位于的块
}
区间修改,区间求和
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)
P1972 [SDOI2009] HH的项链(这题数据量莫队过不了,骗分而已)
某段区间有多少个不同的数
#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;
}