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