[CF 803G]- Periodic RMQ Problem 动态开点线段树 或 离线

CF

题意

有一个长度为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;
}
View Code

 

离线的话,我们可以记录下所有被问到的点,然后我们可以压缩原来长度为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;
}
View Code

 

posted @ 2019-07-17 20:30  ckxkexing  阅读(186)  评论(0编辑  收藏  举报