Loading

分块

分块基本思想:

  1. 将长度为 \(n\) 的序列划分成 \(\sqrt n\) 块,每块的元素个数为 \(\sqrt n\)
  2. 维护 \(pos[i]\) 表示下标 \(i\) 的元素所在块的编号,维护 \(L[i]\)\(R[i]\) 表示第 \(i\) 块的起始下标和中止下标。
  3. 每个块维护相应的信息,比如区间和等。
  4. 维护块的标记 \(tag[i]\),表示第 \(i\) 个块的修改信息
  5. 对于一次询问 \([x, y]\)
    5. 1. 若 \(x\)\(y\) 在同一个块中,则暴力枚举找答案,时间复杂度 \(O(\sqrt n)\)
    5. 2. 若 \(x\)\(y\) 不在同一个块,分为 \(3\) 部分处理:
    3. 2. 1. \(x\) 所在块,暴力枚举, \(O(\sqrt n)\)
    4. 2. 2. \(y\) 所在块,暴力枚举,\(O(\sqrt n)\)
    5. 2. 3. \(x\)\(y\) 中间完整的块,整块枚举, \(O(\sqrt n)\)
    6. 3. 询问时不论是完整的块还是不完整的块,都要读取 \(tag[i]\)
  6. 对于一次修改 \([x, y]\),增加 \(val\)。方式与询问类似。
    6. 1. 枚举完整的块时,修改 \(tag[i]\),否则暴力修改原数组。

LOJ 6277. 数列分块入门 1

image

纯模板

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;
using i64 = long long;

const int N = 50010, V = 310;

int n, s;
int a[N];
int L[V], R[V];
i64 sum[V], tag[V];
int p[N];

void modify(int l, int r, i64 v) {
    int p1 = p[l], p2 = p[r];

    if (p1 == p2) {
        for (int i = l; i <= r; i++)
            a[i] += v;

        sum[p1] += v * (r - l + 1);
    } else {
        for (int i = l; i <= R[p1]; i++)
            a[i] += v;

        sum[p1] += v * (R[p1] - l + 1);

        for (int i = L[p2]; i <= r; i++)
            a[i] += v;

        sum[p2] += v * (r - L[p2] + 1);

        for (int i = p1 + 1; i <= p2 - 1; i++) {
            sum[i] += (R[i] - L[i] + 1) * v;
            tag[i] += v;
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n;
    s = sqrt(n);

    for (int i = 1; i <= n; i++)
        cin >> a[i];

    for (int i = 1; i <= s; i++) {
        L[i] = R[i - 1] + 1;
        R[i] = L[i] + s - 1;
    }

    if (R[s] != n) {
        s++;
        L[s] = L[s - 1] + 1;
        R[s] = n;
    }

    for (int j = 1; j <= s; j++) {
        for (int i = L[j]; i <= R[j]; i++) {
            p[i] = j;
            sum[j] += a[i];
        }
    }

    int T = n;
    int opt, x, y, z;

    while (T--) {
        cin >> opt >> x >> y >> z;

        if (opt) {
            cout << a[y] + tag[p[y]] << '\n';
        } else {
            modify(x, y, z);
        }
    }

    return 0;
}

LOJ 6278. 数列分块入门 2

image

开两个数组 \(a, b\),一个用来统计整块的信息,一个用来统计零碎的部分。

每次更新信息时,两个数组一起更新,不同的是,\(a\) 数组要排序,\(b\) 数组不要排序。

为什么要 \(b\) 数组呢?因为如果排过序后 \([l, r]\) 区间就不准确了,变成排过序后的区间了。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;
using i64 = long long;

const int N = 500010, V = 300;

struct Node {
	i64 x;
	int id;
}a[N], b[N];

bool cmp(const Node a, const Node b) {
	if (a.x != b.x) return a.x < b.x;
	return a.id < b.id; 
}

int n, s;
i64 tag[V];
int L[V];
int R[V];
int p[N];

void update_info(int l, int r) {
	sort(a + l, a + r + 1, cmp);
	for (int j = l; j <= r; j++) 
		b[a[j].id].id = j;
}

void modify(int l, int r, i64 x) {
	int p1 = p[l], p2 = p[r];
	if (p1 == p2) {
		for (int i = l; i <= r; i++) {
			b[i].x += x;
			a[b[i].id].x += x;
		}
		
		update_info(L[p1], R[p1]);
	}
	else {
		for (int i = l; i <= R[p1]; i++) {
			b[i].x += x;
			a[b[i].id].x += x;
		}
		update_info(L[p1], R[p1]);
		for (int i = L[p2]; i <= r; i++) {
			b[i].x += x;
			a[b[i].id].x += x;
		}
		update_info(L[p2], R[p2]);
		for (int i = p1 + 1; i <= p2 - 1; i++) tag[i] += x;
	}
}

int find_value(int l, int r, i64 x, int _tag) {
	int st = l;
	l--, r++;
	while (l + 1 < r) {
		int mid = (l + r) / 2;
		if (a[mid].x + _tag < x) l = mid;
		else r = mid;
	}
	return l - st + 1;
}

int query(int l, int r, i64 z) {
	int p1 = p[l], p2 = p[r];
	if (p1 == p2) {
		int res = 0;
		for (int i = l; i <= r; i++) 
			if (b[i].x + tag[p1] < z)
				res++;
		return res;
	}
	else {
		int res = 0;
		for (int i = l; i <= R[p1]; i++) 
			if (b[i].x + tag[p1] < z)
				res++;
		for (int i = L[p2]; i <= r; i++) 
			if (b[i].x + tag[p2] < z)
				res++;
		for (int i = p1 + 1; i <= p2 - 1; i++)
			res += find_value(L[i], R[i], z, tag[i]);
		return res;
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i].x;
		a[i].id = i;
		b[i].x = a[i].x;
	}
	s = sqrt(n);
	
	for (int i = 1; i <= s; i++) {
		L[i] = R[i - 1] + 1;
		R[i] = L[i] + s - 1;
	} 
	
	if (R[s] != n) {
		s++;
		L[s] = R[s - 1] + 1;
		R[s] = n;
	}
	
	for (int i = 1; i <= s; i++) {
		update_info(L[i], R[i]);
		for (int j = L[i]; j <= R[i]; j++) p[j] = i;
	}
	
	int opt, x, y;
	i64 z;
	for (int i = 1; i <= n; i++) {
		cin >> opt >> x >> y >> z;
		if (opt == 0) modify(x, y, z);
		else cout << query(x, y, z * z) << '\n';
	}
	return 0;
}

LOJ 6279. 数列分块入门 3

image

与上一题类似,只是把求个数变成求最大值。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;
using i64 = long long;

const int N = 100010, V = 410;

int n, s;
int L[V], R[V];
i64 tag[V];
int p[N];

struct Node {
    i64 x;
    int id;
} a[N], b[N];

bool cmp(const Node a, const Node b) {
    if (a.x != b.x)
        return a.x < b.x;

    return a.id < b.id;
}

void update_info(int l, int r) {
    sort(a + l, a + r + 1, cmp);

    for (int i = l; i <= r; i++) {
        b[a[i].id].id = i;
    }
}

void modify(int l, int r, i64 x) {
    int p1 = p[l], p2 = p[r];

    if (p1 == p2) {
        for (int i = l; i <= r; i++) {
            b[i].x += x;
            a[b[i].id].x += x;
        }

        update_info(L[p1], R[p1]);
    } else {
        for (int i = l; i <= R[p1]; i++) {
            b[i].x += x;
            a[b[i].id].x += x;
        }

        update_info(L[p1], R[p1]);

        for (int i = L[p2]; i <= r; i++) {
            b[i].x += x;
            a[b[i].id].x += x;
        }

        update_info(L[p2], R[p2]);

        for (int i = p1 + 1; i <= p2 - 1; i++)
            tag[i] += x;
    }
}

int find_value(int l, int r, i64 x, i64 _tag) {
    l--;
    r++;

    while (l + 1 < r) {
        int mid = (l + r) / 2;

        if (a[mid].x + _tag < x)
            l = mid;
        else
            r = mid;
    }

    return l;
}

int query(int l, int r, i64 x) {
    int p1 = p[l], p2 = p[r];

    if (p1 == p2) {
        i64 res = -1;

        for (int i = l; i <= r; i++) {
            if (b[i].x + tag[p1] < x) {
                res = max(res, b[i].x + tag[p1]);
            }
        }

        return res;
    } else {
        i64 res = -1;

        for (int i = l; i <= R[p1]; i++) {
            if (b[i].x + tag[p1] < x) {
                res = max(res, b[i].x + tag[p1]);
            }
        }

        for (int i = L[p2]; i <= r; i++) {
            if (b[i].x + tag[p2] < x) {
                res = max(res, b[i].x + tag[p2]);
            }
        }

        for (int i = p1 + 1; i <= p2 - 1; i++) {
            int f = find_value(L[i], R[i], x, tag[i]);

            if (f < L[i])
                continue;

            res = max(res, a[f].x + tag[i]);
        }

        return res;
    }
}

int main() {
#ifndef DEBUG
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
#endif

    cin >> n;
    s = sqrt(n);

    for (int i = 1; i <= n; i++) {
        cin >> a[i].x;
        b[i].x = a[i].x;
        a[i].id = i;
    }

    for (int i = 1; i <= s; i++) {
        L[i] = R[i - 1] + 1;
        R[i] = L[i] + s - 1;
    }

    if (R[s] != n) {
        s++;
        L[s] = R[s - 1] + 1;
        R[s] = n;
    }

    for (int j = 1; j <= s; j++) {
        for (int i = L[j]; i <= R[j]; i++)
            p[i] = j;

        update_info(L[j], R[j]);
    }

    int opt, x, y, z;

    for (int i = 1; i <= n; i++) {
        cin >> opt >> x >> y >> z;

        if (opt == 0)
            modify(x, y, z);
        else
            cout << query(x, y, z) << '\n';
    }

    return 0;
}

LOJ 6280. 数列分块入门 4

image

纯模板

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;
using i64 = long long;

const int N = 500010, V = 510;

int n, s;
int a[N], p[N];
int L[V], R[V], tag[V];
i64 sum[V];

int query(int l, int r, const int mod) {
	int p1 = p[l], p2 = p[r];
	if (p1 == p2) {
		i64 res = 0;
		for (int i = l; i <= r; i++) res = (res + a[i] + tag[p1]) % mod;
		return res;
	}
	else {
		i64 res = 0;
		for (int i = l; i <= R[p1]; i++) res = (res + a[i] + tag[p1]) % mod;
		for (int i = L[p2]; i<= r; i++) res = (res + a[i] + tag[p2]) % mod;
		for (int i = p1 + 1; i <= p2 - 1; i++) res = (res + sum[i]) % mod;
		return res;
	}
}

void modify(int l, int r, i64 x) {
	int p1 = p[l], p2 = p[r];
	if (p1 == p2) {
		for (int i = l; i <= r; i++) a[i] += x;
		sum[p1] += x * (r - l + 1);
	} 
	else {
		for (int i = l; i <= R[p1]; i++) a[i] += x;
		sum[p1] += x * (R[p1] - l + 1);
		for (int i = L[p2]; i <= r; i++) a[i] += x;
		sum[p2] += x * (r - L[p2] + 1);
		for (int i = p1 + 1; i <= p2 - 1; i++) {
			tag[i] += x;
			sum[i] += x * (R[i] - L[i] + 1);
		}
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin >> n;
	s = sqrt(n);
	for (int i = 1; i <= n; i++) cin >> a[i];
	for (int i = 1; i <= s; i++) {
		L[i] = R[i - 1] + 1;
		R[i] = L[i] + s - 1;
	}
	if (R[s] != n) {
		s++;
		L[s] = R[s - 1] + 1;
		R[s] = n;
	}
	for (int j = 1; j <= s; j++) {
		for (int i = L[j]; i <= R[j]; i++) {
			sum[j] += a[i];
			p[i] = j;
		}
	}
	
	int opt, x, y, z;
	for (int i = 1; i <= n; i++) {
		cin >> opt >> x >> y >> z;
		if (opt == 1) cout << query(x, y, z + 1) << '\n';
		else modify(x, y, z);
	}
	return 0;
}
posted @ 2023-03-26 11:14  SunnyYuan  阅读(22)  评论(0编辑  收藏  举报