[CF 803G]- Periodic RMQ Problem 动态开点线段树 或 离线
题意
有一个长度为n × k (<=1E9)的数组,有区间修改和区间查询最小值的操作。
思路
由于数组过大,直接做显然不行。
有两种做法,可以用动态开点版本的线段树,或者离线搞(还没搞)(搞好了)。
注意只有1E5次操作,所以真正被更新到的区间并不多,最差单次新开2×log(1E9)。
对于新开的区间的最小值,可以这样计算,如果区间表示的值大于n,那就是原来长度为n的区间的最小值,小于n的话,在ST表中查询即可。
#include <bits/stdc++.h> using namespace std; #define pb push_back #define fi first #define se second #define debug(x) cerr << #x << " := " << x << endl; #define bug cerr << "-----------------------" << endl; #define FOR(a, b, c) for (int a = b; a <= c; ++a) typedef long long ll; typedef long double ld; typedef pair<int, int> pii; typedef pair<ll, ll> pll; template <class T> void _R(T &x) { cin >> x; } void _R(int &x) { scanf("%d", &x); } void _R(ll &x) { scanf("%lld", &x); } void _R(double &x) { scanf("%lf", &x); } void _R(char &x) { scanf(" %c", &x); } void _R(char *x) { scanf("%s", x); } void R() { } template <class T, class... U> void R(T &head, U &... tail) { _R(head); R(tail...); } template <typename T> inline T read(T &x) { x = 0; int f = 0; char ch = getchar(); while (ch < '0' || ch > '9') f |= (ch == '-'), ch = getchar(); while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x = f ? -x : x; } const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; /**********showtime************/ const int maxn = 1e5 + 9; int b[maxn]; int st[maxn][22], Log[maxn]; void init_st(int n) { Log[0] = -1; for (int i = 1; i <= n; i++) { Log[i] = Log[i >> 1] + 1; st[i][0] = b[i]; } for (int j = 1; (1 << j) <= n; j++) { for (int i = 1; i + (1 << j) - 1 <= n; i++) { st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]); } } } int rmq_st(int L, int R) { int k = Log[R - L + 1]; return min(st[L][k], st[R - (1 << k) + 1][k]); } int n, k; int getmin(int le, int ri) { if (ri - le + 1 >= n) return rmq_st(1, n); int L = le % n; if (L == 0) L = n; int R = ri % n; if (R == 0) R = n; if (L <= R) return rmq_st(L, R); return min(rmq_st(L, n), rmq_st(1, R)); } struct Node { int le, ri; int lc, rc; int val, tag; } tree[maxn * 60]; int tot = 0; int newNode(int le, int ri) { tot++; tree[tot].le = le; tree[tot].ri = ri; tree[tot].lc = tree[tot].rc = 0; tree[tot].val = getmin(le, ri); tree[tot].tag = 0; return tot; } void pushdown(int rt) { tree[tree[rt].lc].val = tree[tree[rt].lc].tag = tree[rt].tag; tree[tree[rt].rc].val = tree[tree[rt].rc].tag = tree[rt].tag; tree[rt].tag = 0; } void update(int L, int R, int b, int rt) { if (L <= tree[rt].le && tree[rt].ri <= R) { tree[rt].val = tree[rt].tag = b; return; } int mid = (tree[rt].le + tree[rt].ri) >> 1; if (tree[rt].lc == 0) tree[rt].lc = newNode(tree[rt].le, mid); if (tree[rt].rc == 0) tree[rt].rc = newNode(mid + 1, tree[rt].ri); if (tree[rt].tag) pushdown(rt); if (mid >= L) update(L, R, b, tree[rt].lc); if (mid < R) update(L, R, b, tree[rt].rc); tree[rt].val = min(tree[tree[rt].lc].val, tree[tree[rt].rc].val); } int query(int L, int R, int rt) { if (L <= tree[rt].le && tree[rt].ri <= R) { return tree[rt].val; } int mid = (tree[rt].le + tree[rt].ri) >> 1; if (tree[rt].lc == 0) tree[rt].lc = newNode(tree[rt].le, mid); if (tree[rt].rc == 0) tree[rt].rc = newNode(mid + 1, tree[rt].ri); if (tree[rt].tag) pushdown(rt); int res = inf; if (mid >= L) res = min(res, query(L, R, tree[rt].lc)); if (mid < R) res = min(res, query(L, R, tree[rt].rc)); tree[rt].val = min(tree[tree[rt].lc].val, tree[tree[rt].rc].val); return res; } int main() { scanf("%d%d", &n, &k); for (int i = 1; i <= n; i++) scanf("%d", &b[i]); init_st(n); int q; scanf("%d", &q); newNode(1, n * k); while (q--) { int op; scanf("%d", &op); if (op == 1) { int le, ri, x; scanf("%d%d%d", &le, &ri, &x); update(le, ri, x, 1); } else { int le, ri; scanf("%d%d", &le, &ri); printf("%d\n", query(le, ri, 1)); } } return 0; }
离线的话,我们可以记录下所有被问到的点,然后我们可以压缩原来长度为1E9的数组:问到的点保持不变,而相邻两点之间的区间压缩成一个点,保存这段区间的最小值即可。
#include <iostream> #include <vector> #include <queue> #include <algorithm> using namespace std; #define pb push_back #define fi first #define se second #define debug(x) cerr << #x << " := " << x << endl; #define bug cerr << "-----------------------" << endl; #define FOR(a, b, c) for (int a = b; a <= c; ++a) typedef long long ll; typedef long double ld; typedef pair<int, int> pii; typedef pair<ll, ll> pll; template <class T> void _R(T &x) { cin >> x; } void _R(int &x) { scanf("%d", &x); } void _R(ll &x) { scanf("%lld", &x); } void _R(double &x) { scanf("%lf", &x); } void _R(char &x) { scanf(" %c", &x); } void _R(char *x) { scanf("%s", x); } void R() { } template <class T, class... U> void R(T &head, U &... tail) { _R(head); R(tail...); } template <typename T> inline T read(T &x) { x = 0; int f = 0; char ch = getchar(); while (ch < '0' || ch > '9') f |= (ch == '-'), ch = getchar(); while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x = f ? -x : x; } const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; /**********showtime************/ const int maxn = 1e5 + 9; int b[maxn]; struct Query { int op; int le, ri; int x; } ask[maxn]; vector<int> v, nv, node; int n, k; int st[maxn][20]; int Log[maxn]; void init_st(int n) { Log[0] = -1; for (int i = 1; i <= n; i++) { st[i][0] = b[i]; Log[i] = Log[i >> 1] + 1; } for (int j = 1; (1 << j) <= n; j++) { for (int i = 1; i + (1 << j) - 1 <= n; i++) { st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]); } } } int get(int L, int R) { int k = Log[R - L + 1]; return min(st[L][k], st[R - (1 << k) + 1][k]); } int getmin(int le, int ri) { if (ri - le + 1 >= n) return get(1, n); int l = le % n; if (!l) l = n; int r = ri % n; if (!r) r = n; if (l <= r) return get(l, r); return min(get(l, n), get(1, r)); } int getid(int val) { return lower_bound(nv.begin(), nv.end(), val) - nv.begin() + 1; } int mn[maxn * 16], lazy[maxn * 16]; void build(int le, int ri, int rt) { if (le == ri) { mn[rt] = node[le - 1]; return; } int mid = (le + ri) >> 1; build(le, mid, rt << 1); build(mid + 1, ri, rt << 1 | 1); mn[rt] = min(mn[rt << 1], mn[rt << 1 | 1]); } void pushdown(int rt) { mn[rt << 1] = mn[rt << 1 | 1] = lazy[rt]; lazy[rt << 1] = lazy[rt << 1 | 1] = lazy[rt]; lazy[rt] = 0; } void update(int L, int R, int x, int le, int ri, int rt) { if (le >= L && ri <= R) { lazy[rt] = x; mn[rt] = x; return; } if (lazy[rt]) pushdown(rt); int mid = (le + ri) >> 1; if (mid >= L) update(L, R, x, le, mid, rt << 1); if (mid < R) update(L, R, x, mid + 1, ri, rt << 1 | 1); mn[rt] = min(mn[rt << 1], mn[rt << 1 | 1]); } int query(int L, int R, int le, int ri, int rt) { if (le >= L && ri <= R) { return mn[rt]; } if (lazy[rt]) pushdown(rt); int mid = (le + ri) >> 1; int res = inf; if (mid >= L) res = min(res, query(L, R, le, mid, rt << 1)); if (mid < R) res = min(res, query(L, R, mid + 1, ri, rt << 1 | 1)); mn[rt] = min(mn[rt << 1], mn[rt << 1 | 1]); return res; } int main() { scanf("%d%d", &n, &k); for (int i = 1; i <= n; i++) scanf("%d", &b[i]); init_st(n); int q; scanf("%d", &q); for (int i = 1; i <= q; i++) { int op; scanf("%d", &op); if (op == 1) { ask[i].op = op; scanf("%d%d%d", &ask[i].le, &ask[i].ri, &ask[i].x); v.pb(ask[i].le); v.pb(ask[i].ri); } else { ask[i].op = op; scanf("%d%d", &ask[i].le, &ask[i].ri); v.pb(ask[i].le); v.pb(ask[i].ri); } } sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end()); int N = v.size(); // cout<<" ** "<<endl; for (int i = 0; i < N; i++) { node.pb(getmin(v[i], v[i])); nv.pb(v[i]); if (i + 1 < N && v[i] + 1 <= v[i + 1] - 1) { node.pb(getmin(v[i] + 1, v[i + 1] - 1)); nv.pb(v[i] + 1); } } /// 把一个开区间当成一个点。 N = nv.size(); build(1, N, 1); for (int i = 1; i <= q; i++) { if (ask[i].op == 1) { update(getid(ask[i].le), getid(ask[i].ri), ask[i].x, 1, N, 1); } else { printf("%d\n", query(getid(ask[i].le), getid(ask[i].ri), 1, N, 1)); } } return 0; }
skr