1. 程式人生 > 實用技巧 >數列分塊

數列分塊

數列分塊入門1

區間修改+單點查詢
可以維護一個lazy,分成好多塊之後。
區間內覆蓋的整個塊的直接用每個塊的lazy+c, 沒有覆蓋整個塊了就直接暴力修改。
複雜度\(O(n \ \sqrt n)\)

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define N 50010
#define M 1010

using namespace std;
int n, tot, ans, block, num;
int l[N], r[N], belong[N];
ll a[N], lazy[N];

int read() {
	int s = 0, f = 0; char ch = getchar();
	while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
	return f ? -s : s;
}

void build() {
	block = sqrt(n);
	num = n / block; if (n % block) num++;
	for (int i = 1; i <= num; i++) 
		l[i] = (i - 1) * block + 1, r[i] = i * block;
	r[num] = n;
	for (int i = 1; i <= n; i++)
		belong[i] = (i - 1) / block + 1;
}

void update(int x, int y, int c) {
	if (belong[x] == belong[y]) {
		for (int i = x; i <= y; i++) a[i] += c;
		return;
	}
	for (int i = x; i <= r[belong[x]]; i++) a[i] += c;
	for (int i = l[belong[y]]; i <= y; i++) a[i] += c;
	for (int i = belong[x] + 1; i <= belong[y] - 1; i++) lazy[i] += c;
}

ll query(int x) {
	return a[x] + lazy[belong[x]];
}

int main() {
	n = read();
	for (int i = 1; i <= n; i++) a[i] = read();
	build();
	for (int i = 1, opt, x, y, c; i <= n; i++) {
		opt = read(), x = read(), y = read(), c = read();
		if (opt == 0) update(x, y, c);
		else printf("%lld\n", query(y));
	}
	return 0;
}

數列分塊入門2

區間修改
查詢區間l, r內有多少個比c 小的數

區間修改的話就按照上邊那種方法修改。
查詢我們可以一開始就給每一個塊內的答案排序,然後二分查詢就行了。
遇到不是完整的塊直接暴力修改,然後重新排序。

#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define N 50010
#define M 1010

using namespace std;
int n, m, num, block;
int l[N], r[N], belong[N]; int lazy[N], a[N];
vector<ll> vec[N];

int read() {
	int s = 0, f = 0; char ch = getchar();
	while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
	return f ? -s : s;
}

void build() {
	block = sqrt(n);
	num = n / block; if (n % block) num++;
	r[num] = n;
	for (int i = 1; i <= n; i++)
		belong[i] = (i - 1) / block + 1, vec[belong[i]].push_back(a[i]);
	for (int i = 1; i <= num; i++) {
		l[i] = (i - 1) * block + 1, r[i] = i * block;
		sort(vec[i].begin(), vec[i].end());
	}
}

void resort(int x) {
	vec[x].clear();
	for (int i = l[x]; i <= r[x]; i++) vec[x].push_back(a[i]);
	sort(vec[x].begin(), vec[x].end());
}

void update(int x, int y, int c) {
	if (belong[x] == belong[y]) {
		for (int i = x; i <= y; i++) a[i] += c;
		resort(belong[x]);
		return;
	}
	for (int i = x; i <= r[belong[x]]; i++) a[i] += c;
	for (int i = l[belong[y]]; i <= y; i++) a[i] += c;
	for (int i = belong[x] + 1; i <= belong[y] - 1; i++) lazy[i] += c;
	resort(belong[x]), resort(belong[y]);
}

int query(int x, int y, int c) {
	int ans = 0;
	if (belong[x] == belong[y]) {
		for (int i = x; i <= y; i++)
			if (a[i] + lazy[belong[x]] < c) ans++;
		return ans;
	}
	for (int i = x; i <= r[belong[x]]; i++)
		if (a[i] + lazy[belong[x]] < c) ans++;
	for (int i = l[belong[y]]; i <= y; i++)
		if (a[i] + lazy[belong[y]] < c) ans++;
	for (int i = belong[x] + 1; i <= belong[y] - 1; i++)
		ans += lower_bound(vec[i].begin(), vec[i].end(), c - lazy[i]) - vec[i].begin();
	return ans;
}

int main() {
	n = read();
	for (int i = 1; i <= n; i++) a[i] = read();
	for (int i = 1, opt, x, y, c; i <= n; i++) {
		opt = read(), x = read(), y = read(), c = read();
		if (opt == 0) update(x, y, c);
		else printf("%d\n", query(x, y, c * c));
	}
	return 0;
}

數列分塊入門3

區間修改+查詢區間內某個數的前驅
和上邊那種做法類似。

#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define N 100010
#define M 1010

using namespace std;
int n, num, block;
int l[N], r[N], lazy[N], belong[N], a[N];
vector<int> vec[N];

int read() {
	int s = 0, f = 0; char ch = getchar();
	while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
	return f ? -s : s;
}

void build() {
	block = sqrt(n);
	num = n / block; if (n % block) num++;
	r[num] = n;
	for (int i = 1; i <= n; i++)
		belong[i] = (i - 1) / block + 1, vec[belong[i]].push_back(a[i]);
	for (int i = 1; i <= num; i++) {
		l[i] = (i - 1) * block + 1, r[i] = i * block;
		sort(vec[i].begin(), vec[i].end());
	}
}

void resort(int x) {
	vec[x].clear();
	for (int i = l[x]; i <= r[x]; i++) vec[x].push_back(a[i]);
	sort(vec[x].begin(), vec[x].end());
}

void update(int x, int y, int c) {
	if (belong[x] == belong[y]) {
		for (int i = x; i <= y; i++) a[i] += c;
		resort(belong[x]);
		return;
	}
	for (int i = x; i <= r[belong[x]]; i++) a[i] += c;
	for (int i = l[belong[y]]; i <= y; i++) a[i] += c;
	for (int i = belong[x] + 1; i <= belong[y] - 1; i++) lazy[i] += c;
	resort(belong[x]), resort(belong[y]);
}

int query(int x, int y, int c) {
	int ans = -1;
	if (belong[x] == belong[y]) {
		for (int i = x; i <= y; i++)
			if (a[i] + lazy[belong[x]] < c)
				ans = max(ans, a[i] + lazy[belong[i]]);
		return ans;
	}
	for (int i = x; i <= r[belong[x]]; i++)
		if (a[i] + lazy[belong[i]] < c) ans = max(ans, a[i] + lazy[belong[i]]);
	for (int i = l[belong[y]]; i <= y; i++)
		if (a[i] + lazy[belong[i]] < c) ans = max(ans, a[i] + lazy[belong[i]]);
	for (int i = belong[x] + 1; i <= belong[y] - 1; i++) {
		int pos = lower_bound(vec[i].begin(), vec[i].end(), c - lazy[i]) - vec[i].begin();
		if (pos >= 1) ans = max(ans, vec[i][pos - 1] + lazy[i]);
	}
	return ans;
}

int main() {
	n = read();
	for (int i = 1; i <= n; i++) a[i] = read();
	build();
	for (int i = 1, opt, x, y, c; i <= n; i++) {
		opt = read(), x = read(), y = read(), c = read();
		if (opt == 0) update(x, y, c);
		else printf("%d\n", query(x, y, c));
	}
	return 0;
}

數列分塊入門4

區間修改+區間查詢
可以開一個sum來存每一個塊裡的權值和

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define N 50010
#define M 1010

using namespace std;
int n, num, block;
int l[N], r[N], belong[N]; ll sum[N], lazy[N], a[N];

int read() {
	int s = 0, f = 0; char ch = getchar();
	while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
	return f ? -s : s;
}

void build() {
	block = sqrt(n);
	num = n / block; if (n % block) num++;
	r[num] = n;
	for (int i = 1; i <= n; i++) belong[i] = (i - 1) / block + 1;
	for (int i = 1; i <= num; i++) 
		l[i] = (i - 1) * block + 1, r[i] = i * block;
	for (int i = 1; i <= num; i++)
		for (int j = l[i]; j <= r[i]; j++)
			sum[i] += a[j];
}

void update(int x, int y, ll c) {
	if (belong[x] == belong[y]) {
		for (int i = x; i <= y; i++) a[i] += c;
		sum[belong[x]] += (y - x + 1) * c;
		return;
	}
	for (int i = x; i <= r[belong[x]]; i++) a[i] += c;
	for (int i = l[belong[y]]; i <= y; i++) a[i] += c;
	for (int i = belong[x] + 1; i <= belong[y] - 1; i++) lazy[i] += c;
	sum[belong[x]] += (r[belong[x]] - x + 1) * c;
	sum[belong[y]] += (y - l[belong[y]] + 1) * c;
}

ll query(int x, int y, ll c) {
	ll mod = c + 1, ans = 0;
	if (belong[x] == belong[y]) {
		for (int i = x; i <= y; i++)
			ans = (ans + a[i] + lazy[belong[i]]) % mod;
		return ans;
	}
	for (int i = x; i <= r[belong[x]]; i++) 
		ans = (ans + a[i] + lazy[belong[i]]) % mod;
	for (int i = l[belong[y]]; i <= y; i++) 
		ans = (ans + a[i] + lazy[belong[i]]) % mod;
	for (int i = belong[x] + 1; i <= belong[y] - 1; i++) 
		ans = (ans + sum[i] + block * lazy[i]) % mod;
	return ans;
}

signed main() {
	n = read();
	for (int i = 1; i <= n; i++) a[i] = read();
	build();
	for (int i = 1, opt, x, y, c; i <= n; i++) {
		opt = read(), x = read(), y = read(), c = read();
		if (opt == 0) update(x, y, c);
		else printf("%lld\n", query(x, y, c));
	}
	return 0;
}

數列分塊入門5

區間開平方。
眾所周知\(\sqrt{\sqrt{\sqrt{\sqrt{\sqrt {2^{32}}}}}} = 1\)
所以一個區間開平方開5次就差不多是1或者0
這樣的區間不需要再開方。