分块

BZOJ4491 区间最长不上升子串

点击查看代码
// BZOJ 4491
// 差分, 转化为连续区间上最长 >=0 或 <=0 的区间
// 每个节点维护区间 前缀最大值, 后缀最大值, 区间答案, 区间长度
// 类似“小白逛公园”
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <utility>
#include <queue>

using namespace std;

const int N = 50005;

int n, m;
int val[N], tmp[N]; // val 为原来数组, tmp 为差分数组

struct SegTree {
	struct Node {
		int lmax, rmax, res, cnt; // 前缀最大值, 后缀最大值, 区间答案, 区间长度
	} tr[N * 4];
	// 合并区间
	static Node merge(const Node &l, const Node &r) {
		return {
			(l.lmax == l.cnt ? l.cnt + r.lmax : l.lmax), // 左边是否全部 >=0 或 <=0 ?
			(r.rmax == r.cnt ? r.cnt + l.rmax : r.rmax), // 右边是否全部 >=0 或 <=0 ?
			max(l.rmax + r.lmax, max(l.res, r.res)), // res 的组成部分
			l.cnt + r.cnt
		};
	}
	static inline int ls(int u) { return u << 1; }
	static inline int rs(int u) { return u << 1 | 1; }
	// 建树
	void build(int u, int l, int r) {
		if(l == r) // 叶子节点: 初始值与 tmp[u]>=0 有关
			tr[u].cnt = 1, tr[u].lmax = tr[u].rmax = tr[u].res = (tmp[l] >= 0);
		else {
			int mid = (l + r) >> 1;
			build(ls(u), l, mid);
			build(rs(u), mid + 1, r);
			tr[u] = merge(tr[ls(u)], tr[rs(u)]); // 合并左儿子和右儿子的信息
		}
	}
	// 区间查询
	Node query(int u, int l, int r, int x, int y) {
		if(x <= l && r <= y) return tr[u];
		else {
			int mid = (l + r) >> 1;
			if(y <= mid) return query(ls(u), l, mid, x, y);
			if(x > mid) return query(rs(u), mid + 1, r, x, y);
			return merge(query(ls(u), l, mid, x, y), query(rs(u), mid + 1, r, x, y));
		}
	}
} tr1, tr2; // 差分>=0 和 差分<=0

inline int read() {
	int x = 0, f = 0, c = getchar();
	while(!isdigit(c)) {
		if(c == '-') f = 1;
		c = getchar();
	}
	while(isdigit(c)) {
		x = (x << 1) + (x << 3) + (c ^ 48);
		c = getchar();
	}
	return f ? -x : x;
}

int main() {
	// scanf("%d", &n);
	n = read();
	for(int i = 1; i <= n; i ++) /* scanf("%d", val + i); */val[i] = read();
	for(int i = 1; i <= n; i ++) tmp[i] = val[i] - val[i - 1];
	tr1.build(1, 1, n);
	for(int i = 1; i <= n; i ++) tmp[i] = val[i - 1] - val[i];
	tr2.build(1, 1, n);
	// scanf("%d", &m);
	m = read();
	for(int i = 1, l, r; i <= m; i ++) {
		// scanf("%d%d", &l, &r);
		l = read(), r = read();
		if(l == r) puts("1");
		else
			printf("%d\n", max(tr1.query(1, 1, n, l + 1, r).res, tr2.query(1, 1, n, l + 1, r).res) + 1);
	}
	return 0;
}

P3203 HNOI2010弹飞绵羊

分块: 每一个块中每个点记录step[i]和to[i]表示跳出块内的步数和跳出块的点的编号

点击查看代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <utility>
#include <array>
#include <queue>
#include <math.h>

using namespace std;

const int N = 1e6 + 5, M = 1e3 + 5;

int n, m, k[N], len;
int step[N], to[N]; // 若 to[i] > n 则被弹飞

inline int get_id(int x) { return (x - 1) / len + 1; }

int main() {
	scanf("%d", &n), len = sqrt(n);
	for(int i = 1; i <= n; i ++) scanf("%d", k + i);
	for(int i = n; i; i --) {
		int j = i + k[i];
		if(j > n) step[i] = 1, to[i] = 0; // 弹飞了
		else if(get_id(i) == get_id(j)) step[i] = step[j] + 1, to[i] = to[j]; // 还在本块
		else step[i] = 1, to[i] = j; // 弹飞此块
	}
	scanf("%d", &m);
	for(int i = 1, op, a, b; i <= m; i ++) {
		scanf("%d%d", &op, &a), a ++;
		if(op == 1) {
			int res = 0;
			while(a) res += step[a], a = to[a];
			printf("%d\n", res);
		} else {
			scanf("%d", &b);
			k[a] = b;
			for(int i = a, l = (get_id(a) - 1) * len; i > l; i --) {
				int j = i + k[i];
				if(j > n) step[i] = 1, to[i] = 0;
				else if(get_id(i) == get_id(j)) step[i] = step[j] + 1, to[i] = to[j];
				else step[i] = 1, to[i] = j;
			}
		}
	}
	return 0;
}

P3645 APIO2015 雅加达的摩天楼

Q5.2.1.2. 雅加达的摩天楼
设k=sqrt(n/3)
p<=k的分层图(层为步数);p>=k的直接连边(最底层)

点击查看代码

#include <math.h>
#include <stdio.h>
#include <string.h>
const int N = 3.1e6 + 5, M = 18e6 + 5;
int n, m, k;
int h[N], e[M], w[M], nxt[M], idx;
int q[N], dist[N];
bool st[N];
void add(int a, int b, int c) {
	e[++ idx] = b, w[idx] = c, nxt[idx] = h[a], h[a] = idx;
}
int SPFA(int s, int t) {
	int hh = 0, tt = 0;
	memset(dist, 0x3f, sizeof(dist));
	q[tt ++] = s, dist[s] = 0, st[s] = true;
	while(hh != tt) {
		int u = q[hh ++];
		hh == N && (hh = 0);
		st[u] = false;
		for(int i = h[u]; i; i = nxt[i]) {
			int v = e[i];
			if(dist[v] > dist[u] + w[i]) {
				dist[v] = dist[u] + w[i];
				if(!st[v]) {
					q[tt ++] = v;
					tt == N && (tt = 0);
					st[v] = true;
				}
			}
		}
	}
	if(dist[t] >= 0x3f3f3f3f) return -1;
	return dist[t];
}
inline int id(int i, int j) {
	return i * n + j;
}
int main() {
	scanf("%d%d", &n, &m), k = sqrt(n / 3.);
	for(int i = 1; i <= k; i ++)
		for(int j = 0; j < n; j ++) {
			add(id(i, j), j, 0);
			if(i + j >= n) break;
			add(id(i, j), id(i, j + i), 1);
			add(id(i, j + i), id(i, j), 1);
		}
	int s, t;
	for(int i = 0, b, p; i < m; i ++) {
		scanf("%d%d", &b, &p);
		if(p <= k) add(b, id(p, b), 0);
		else {
			for(int j = 1; b + j * p < n; j ++) add(b, b + j * p, j);
			for(int j = 1; b - j * p >= 0; j ++)add(b, b - j * p, j);
		}
		if(i == 0) s = b;
		if(i == 1) t = b;
	}
	for(int i = 1; i <= k; i ++)
		for(int j = 0; j < n; j ++)
			if(h[id(i, j)]) add(id(i, j), j, 0);
	printf("%d\n", SPFA(s, t));
	return 0;
}

P4168 Violet蒲公英

点击查看代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <utility>
#include <array>
#include <queue>
#include <math.h>
#include <assert.h>

using namespace std;

const int N = 4e4 + 5, M = 2e2 + 5;

int n, m, len;
vector<int> all;
int num[N]; // 离散化值, 从1开始
int oldval[N]; // 离散化对应的原值
int f[M][M]; // f[i][j] 表示第 i 块到第 j 块的最小众数(离散化后)
int s[M][N]; // s[i][j] 表示第 1 块到第 i 块中 j 出现的次数(离散化后)
int blockcnt, valcnt; // 块的个数, 值的个数
int bucket[N]; // 桶

inline int get_id(int x) { return (x - 1) / len + 1; }

int calc(int l, int r) {
	int bL = get_id(l), bR = get_id(r), res = 0;
	if(bR - bL <= 1) { // 小于等于两格: 暴力
		for(int i = l; i <= r; i ++) bucket[num[i]] ++;
		for(int i = l; i <= r; i ++)
			if(bucket[num[i]] > bucket[res] || (bucket[num[i]] == bucket[res] && num[i] < res)) res = num[i];
		for(int i = l; i <= r; i ++) bucket[num[i]] = 0; // 清空
	} else {
		for(int i = l; i <= len * bL; i ++) bucket[num[i]] ++;
		for(int i = len * (bR - 1) + 1; i <= r; i ++) bucket[num[i]] ++;
		res = f[bL + 1][bR - 1]; // 初值为完整区间的答案
		int cnt = s[bR - 1][res] - s[bL][res] + bucket[res];
		for(int i = l; i <= len * bL; i ++) {
			int now = s[bR - 1][num[i]] - s[bL][num[i]] + bucket[num[i]];
			if(now > cnt || (now == cnt && num[i] < res)) res = num[i], cnt = now;
		}
		for(int i = len * (bR - 1) + 1; i <= r; i ++) {
			int now = s[bR - 1][num[i]] - s[bL][num[i]] + bucket[num[i]];
			if(now > cnt || (now == cnt && num[i] < res)) res = num[i], cnt = now;
		}
		for(int i = l; i <= len * bL; i ++) bucket[num[i]] = 0;
		for(int i = len * (bR - 1) + 1; i <= r; i ++) bucket[num[i]] = 0;
	}
	return oldval[res];
}

int main() {
	scanf("%d%d", &n, &m), len = sqrt(n), blockcnt = get_id(n) + 1;
	for(int i = 1; i <= n; i ++) scanf("%d", num + i), all.push_back(num[i]);
	// 离散化
	sort(all.begin(), all.end()), all.erase(unique(all.begin(), all.end()), all.end());
	valcnt = all.size();
	for(int i = 0; i < valcnt; i ++) oldval[i + 1] = all[i];
	for(int i = 1; i <= n; i ++) num[i] = lower_bound(all.begin(), all.end(), num[i]) - all.begin() + 1;
	// 预处理 s
	for(int i = 1; i <= blockcnt; i ++) {
		for(int j = 1; j <= valcnt; j ++) s[i][j] += s[i - 1][j]; // 加上之前的
		for(int j = len * (i - 1) + 1; j <= min(len * i, n); j ++) s[i][num[j]] ++;
	}
	// 预处理 f
	for(int i = 1; i <= blockcnt; i ++)
		for(int j = i; j <= blockcnt; j ++) {
			int res = f[i][j - 1], cnt = s[j][res] - s[i - 1][res]; // 初始值为之前的最大值
			for(int k = len * (j - 1) + 1; k <= min(len * j, n); k ++) {
				int now = s[j][num[k]] - s[i - 1][num[k]];
				if(cnt < now || (cnt == now && num[k] < res)) res = num[k], cnt = now;
			}
			f[i][j] = res;
		}
	for(int i = 1, l, r, x = 0; i <= m; i ++) {
		scanf("%d%d", &l, &r);
		l = (l + x - 1) % n + 1, r = (r + x - 1) % n + 1;
		if(l > r) swap(l, r);
		printf("%d\n", x = calc(l, r));
	}
	return 0;
}

P1975 [国家集训队]排队

交换: 1. 若 a[l] < a[r] 则 ans ++ 否则 ans --
2. 对于 l < k < r 有
1. 若 a[l] < a[k] 则 ans ++ 否则 ans --
2. 若 a[r] > a[k] 则 ans ++ 否则 ans --
用分块维护

点击查看代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <utility>
#include <array>
#include <queue>
#include <math.h>

using namespace std;

const int N = 2e4 + 5, M = 2e2 + 5;

int n, m, len, blockcnt;
int val[N], aaa[N];
int tmp[N];
vector<int> sorted[M];

inline int get_id(int x) { return (x - 1) / len + 1; }

// 归并排序
int solve(int l, int r) {
	if(l >= r) return 0;
	int mid = (l + r) >> 1;
	int res = solve(l, mid) + solve(mid + 1, r);
	int i = l, j = mid + 1, k = l;
	while(k <= r)
		if((i <= mid && aaa[i] <= aaa[j]) || j > r) tmp[k ++] = aaa[i ++];
		else res += mid - i + 1, tmp[k ++] = aaa[j ++];
	for(i = l; i <= r; i ++) aaa[i] = tmp[i];
	return res;
}

int main() {
	scanf("%d", &n), len = sqrt(n), blockcnt = get_id(n);
	for(int i = 1; i <= n; i ++) scanf("%d", val + i), aaa[i] = val[i];
	int ans = solve(1, n);
	printf("%d\n", ans);
	for(int i = 1; i <= n; i ++) sorted[get_id(i)].push_back(val[i]);
	for(int i = 1; i <= blockcnt; i ++) sort(sorted[i].begin(), sorted[i].end());
	scanf("%d", &m);
	for(int T = 1, l, r; T <= m; T ++) {
		scanf("%d%d", &l, &r);
		if(l > r) swap(l, r);
		if(val[l] != val[r]) {
			int L = get_id(l), R = get_id(r);
			ans += int(val[l] < val[r]) - int(val[l] > val[r]);
			if(L == R) {
				for(int k = l + 1; k < r; k ++)
					ans += int(val[k] > val[l]) - int(val[k] < val[l]),
					ans -= int(val[k] > val[r]) - int(val[k] < val[r]);
				swap(val[l], val[r]);
			} else {
				for(int k = l + 1; k <= L * len; k ++)
					ans += int(val[k] > val[l]) - int(val[k] < val[l]),
					ans -= int(val[k] > val[r]) - int(val[k] < val[r]);
				for(int k = (R - 1) * len + 1; k < r; k ++)
					ans += int(val[k] > val[l]) - int(val[k] < val[l]),
					ans -= int(val[k] > val[r]) - int(val[k] < val[r]);
				for(int k = L + 1; k < R; k ++) {
					ans -= lower_bound(sorted[k].begin(), sorted[k].end(), val[l]) - sorted[k].begin() - 1;
					ans += int(sorted[k].size()) - (upper_bound(sorted[k].begin(), sorted[k].end(), val[l]) - sorted[k].begin()) + 1;
					ans -= int(sorted[k].size()) - (upper_bound(sorted[k].begin(), sorted[k].end(), val[r]) - sorted[k].begin()) + 1;
					ans += lower_bound(sorted[k].begin(), sorted[k].end(), val[r]) - sorted[k].begin() - 1;
				}
				swap(val[l], val[r]);
				sorted[L].clear(), sorted[R].clear();
				for(int k = (L - 1) * len + 1; k <= min(n, L * len); k ++) sorted[L].push_back(val[k]);
				for(int k = (R - 1) * len + 1; k <= min(n, R * len); k ++) sorted[R].push_back(val[k]);
				sort(sorted[L].begin(), sorted[L].end()), sort(sorted[R].begin(), sorted[R].end());
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}

P4135 作诗

// 类似区间众数查询: 分块
// cnt[i][j] 表示前 i 块 j 出现的次数
// ans[i][j] 表示第 i 块到第 j 块中出现偶数次的数的个数

点击查看代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <utility>
#include <queue>
#include <math.h>

using namespace std;

const int N = 1e5 + 5, M = 316 + 5;

int n, m, c, len, blockcnt;
int val[N];
int cnt[M][N]; // cnt[i][j] 表示前 i 块 j 出现的次数
int ans[M][M]; // ans[i][j] 表示第 i 块到第 j 块中出现偶数次的数的个数
int bucket[N]; // 桶

inline int get_id(int x) { return (x - 1) / len + 1; }

int query(int l, int r) {
	int L = get_id(l), R = get_id(r), res = 0;
	if(L == R) {
		for(int k = l; k <= r; k ++) {
			bucket[val[k]] ++;
			if(bucket[val[k]] % 2 == 0) res ++; // 多了一个偶数(这里不会出现 0 个的情况)
			else if(bucket[val[k]] >= 3) res --; // bucket[val[k]]==1 时res并没有++所以不能--
		}
		for(int k = l; k <= r; k ++) bucket[val[k]] = 0;
	} else {
		res = ans[L + 1][R - 1];
		for(int k = l; k <= L * len; k ++) {
			bucket[val[k]] ++;
			int times = bucket[val[k]] + cnt[R - 1][val[k]] - cnt[L][val[k]]; // val[k] 出现的次数
			if(times % 2 == 0) res ++; // 多了一个偶数(这里不会出现 0 个的情况)
			else if(times >= 3) res --; // bucket[val[k]]==1 时res并没有++所以不能--
		}
		for(int k = (R - 1) * len + 1; k <= r; k ++) {
			bucket[val[k]] ++;
			int times = bucket[val[k]] + cnt[R - 1][val[k]] - cnt[L][val[k]]; // val[k] 出现的次数
			if(times % 2 == 0) res ++; // 多了一个偶数(这里不会出现 0 个的情况)
			else if(times >= 3) res --; // bucket[val[k]]==1 时res并没有++所以不能--
		}
		for(int k = l; k <= L * len; k ++) bucket[val[k]] = 0;
		for(int k = (R - 1) * len + 1; k <= r; k ++) bucket[val[k]] = 0;
	}
	return res;
}

int main() {
	scanf("%d%d%d", &n, &c, &m), len = sqrt(n), blockcnt = get_id(n);
	for(int i = 1; i <= n; i ++) scanf("%d", val + i);
	for(int i = 1; i <= blockcnt; i ++) { // 预处理
		for(int j = 0; j <= c; j ++) cnt[i][j] += cnt[i - 1][j];
		for(int j = (i - 1) * len + 1; j <= min(i * len, n); j ++) cnt[i][val[j]] ++;
	}
	for(int i = 1; i <= blockcnt; i ++) { // 在这个层面上使用桶
		for(int j = i; j <= blockcnt; j ++) {
			int res = ans[i][j - 1];
			for(int k = (j - 1) * len + 1; k <= j * len; k ++) {
				bucket[val[k]] ++;
				if(bucket[val[k]] % 2 == 0) res ++; // 多了一个偶数(这里不会出现 0 个的情况)
				else if(bucket[val[k]] >= 3) res --; // bucket[val[k]]==1 时res并没有++所以不能--
			}
			ans[i][j] = res;
		}
		for(int j = 0; j <= c; j ++) bucket[j] = 0;
	}
	for(int i = 1, l, r, x = 0; i <= m; i ++) {
		scanf("%d%d", &l, &r);
		l = (l + x) % n + 1, r = (r + x) % n + 1;
		if(l > r) swap(l, r);
		printf("%d\n", x = query(l, r));
	}
	return 0;
}

M维偏序

posted @ 2022-09-28 14:21  azzc  阅读(63)  评论(0)    收藏  举报