分块
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;
}
浙公网安备 33010602011771号