結果
問題 | No.1234 典型RMQ |
ユーザー | re_re0101 |
提出日時 | 2020-09-18 21:55:10 |
言語 | C++14 (gcc 12.3.0 + boost 1.83.0) |
結果 |
AC
|
実行時間 | 201 ms / 2,000 ms |
コード長 | 18,647 bytes |
コンパイル時間 | 2,171 ms |
コンパイル使用メモリ | 185,100 KB |
実行使用メモリ | 6,400 KB |
最終ジャッジ日時 | 2024-11-09 01:50:52 |
合計ジャッジ時間 | 7,853 ms |
ジャッジサーバーID (参考情報) |
judge5 / judge1 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 2 ms
5,248 KB |
testcase_01 | AC | 2 ms
5,248 KB |
testcase_02 | AC | 2 ms
5,248 KB |
testcase_03 | AC | 2 ms
5,248 KB |
testcase_04 | AC | 3 ms
5,248 KB |
testcase_05 | AC | 2 ms
5,248 KB |
testcase_06 | AC | 187 ms
5,504 KB |
testcase_07 | AC | 153 ms
5,248 KB |
testcase_08 | AC | 201 ms
6,272 KB |
testcase_09 | AC | 176 ms
5,248 KB |
testcase_10 | AC | 195 ms
5,888 KB |
testcase_11 | AC | 190 ms
5,376 KB |
testcase_12 | AC | 173 ms
5,248 KB |
testcase_13 | AC | 156 ms
5,248 KB |
testcase_14 | AC | 176 ms
5,248 KB |
testcase_15 | AC | 166 ms
5,248 KB |
testcase_16 | AC | 193 ms
5,888 KB |
testcase_17 | AC | 177 ms
5,248 KB |
testcase_18 | AC | 151 ms
5,248 KB |
testcase_19 | AC | 200 ms
6,144 KB |
testcase_20 | AC | 149 ms
6,400 KB |
testcase_21 | AC | 189 ms
5,504 KB |
testcase_22 | AC | 160 ms
5,632 KB |
testcase_23 | AC | 157 ms
5,632 KB |
testcase_24 | AC | 159 ms
5,632 KB |
testcase_25 | AC | 162 ms
5,632 KB |
testcase_26 | AC | 160 ms
5,632 KB |
testcase_27 | AC | 2 ms
5,248 KB |
testcase_28 | AC | 2 ms
5,248 KB |
testcase_29 | AC | 2 ms
5,248 KB |
ソースコード
#ifndef ONLINE_JUDGE #define _GLIBCXX_DEBUG #endif #include<bits/stdc++.h> using namespace std; #define rep(i, n) for (ll i = 0; i < (ll)(n); i++) #define bit(n,k) (((ll)n>>(ll)k)&1) /*nのk bit目*/ #define pb push_back #define pf push_front #define FI first #define SE second #define eb emplace_back #define SZ(x) ((ll)(x).size()) #define all(x) (x).begin(),(x).end() #define rall(x) (x).rbegin(),(x).rend() #define PI 3.14159265359 const double eps = 1e-12; const long long INF= 1e+18+1; //long long INF=(1LL<<31)-1; typedef long long ll; typedef vector<ll> vl; typedef vector<vector<ll> >vvl; typedef pair<ll,ll> P; typedef tuple<ll,ll,ll> T; typedef struct Point_Coordinates { ll x, y; } point; const ll MOD=1000000007LL; //ll MOD=1000000007LL; //const ll MOD=998244353LL; //const ll MOD=1777777777LL; //const ll MAX_V=114514LL; //const ll MAX = 500010LL; const ll mod=MOD; string abc="abcdefghijklmnopqrstuvwxyz"; string ABC="ABCDEFGHIJKLMNOPQRSTUVWXYZ"; vl dx={0,0,1,-1}; vl dy={1,-1,0,0}; /*struct edge{ ll from; ll to; ll cost; };*/ //素因数分解O(√n) map<ll,ll>prime_factor(ll n){ map<ll,ll>res; for(ll i=2;i*i<=n;i++){ while(n%i==0){ res[i]++; n/=i; } } if(n!=1)res[n]=1; return res; } const ll MAX = 2000010; long long fac[MAX], finv[MAX], inv[MAX]; //finvが階乗の逆元 // テーブルを作る前処理 void COMinit() { fac[0] = fac[1] = 1; finv[0] = finv[1] = 1; inv[1] = 1; for (ll i = 2; i < MAX; i++){ fac[i] = fac[i - 1] * i % MOD; inv[i] = MOD - inv[MOD%i] * (MOD / i) % MOD; finv[i] = finv[i - 1] * inv[i] % MOD; } } // 二項係数計算 long long COM(ll n, ll k){ if (n < k) return 0; if (n < 0 || k < 0) return 0; return fac[n] * (finv[k] * finv[n - k] % MOD) % MOD; } ll modpow(ll a, ll n) { ll res = 1; while (n > 0) { if (n & 1) res = res * a % mod; a = a * a % mod; n >>= 1; } return res; } /*Eratosthenes() ll N=2000010; vl arr(N); void Eratosthenes(){ for(ll i = 0; i < N; i++){ arr[i] = 1; } arr[1]=0; for(ll i = 2; i < sqrt(N); i++){ if(arr[i]){ for(ll j = 0; i * (j + 2) < N; j++){ arr[i *(j + 2)] = 0; } } } }*/ //素数判定O(√n) bool is_prime(ll n){ for(ll i=2;i*i<=n;i++){ if(n%i==0)return false; } return n!=1; } //約数の列挙O(√n) vector<ll>divisor(ll n){ vector<ll>res; for(ll i=1;i*i<=n;i++){ if(n%i==0){ res.push_back(i); if(i != n/i) res.push_back(n/i); } } return res; } /* SegTree<X>(n,fx,ex): モノイド(集合X, 二項演算fx, 単位元ex)についてサイズnで構築 set(int i, X x), build(): i番目の要素をxにセット。まとめてセグ木を構築する。O(n) update(i,x): i 番目の要素を x に更新。O(log(n)) query(a,b): [a,b) 全てにfxを作用させた値を取得。O(log(n)) */ template <typename X> struct SegTree { using FX = function<X(X, X)>; int n; FX fx; const X ex; vector<X> dat; SegTree(int n_, FX fx_, X ex_) : n(), fx(fx_), ex(ex_), dat(n_ * 4, ex_) { int x = 1; while (n_ > x) { x *= 2; } n = x; } void set(int i, X x) { dat[i + n - 1] = x; } void build() { for (int k = n - 2; k >= 0; k--) dat[k] = fx(dat[2 * k + 1], dat[2 * k + 2]); } void update(int i, X x) { i += n - 1; dat[i] = x; while (i > 0) { i = (i - 1) / 2; // parent dat[i] = fx(dat[i * 2 + 1], dat[i * 2 + 2]); } } // the minimum element of [a,b) X query(int a, int b) { return query_sub(a, b, 0, 0, n); } X query_sub(int a, int b, int k, int l, int r) { if (r <= a || b <= l) { return ex; } else if (a <= l && r <= b) { return dat[k]; } else { X vl = query_sub(a, b, k * 2 + 1, l, (l + r) / 2); X vr = query_sub(a, b, k * 2 + 2, (l + r) / 2, r); return fx(vl, vr); } } /* debug */ inline X operator[](int a) { return query(a, a + 1); } void print() { for (int i = 0; i < 2 * n - 1; ++i) { cout << (*this)[i]; if (i != n) cout << ","; } cout << endl; } /* 使用例 auto fx=[](int x1,int x2)->int{return max(x1,x2);}; ll ex=0; SegTree<ll>rmq(n,fx,ex);*/ }; /* Trie 木: 文字の種類(char_size)、int型で0に対応する文字(base) insert(word): 単語 word を Trie 木に挿入する search(word): 単語 word が Trie 木にあるか判定する start_with(prefix): prefix が一致する単語が Trie 木にあるか判定する count(): 挿入した単語の数を返す size(): Trie 木の頂点数を返す 計算量:insert, search ともに O(M)(Mは単語の長さ) */ template <int char_size, int base> struct Trie { struct Node { // 頂点を表す構造体 vector<int> next; // 子の頂点番号を格納。存在しなければ-1 vector<int> accept; // 末端がこの頂点になる単語の word_id を保存 int c; // base からの間隔をint型で表現したもの int common; // いくつの単語がこの頂点を共有しているか Node(int c_) : c(c_), common(0) { next.assign(char_size, -1); } }; vector<Node> nodes; // trie 木本体 int root; Trie() : root(0) { nodes.push_back(Node(root)); } // 単語の挿入 void insert(const string &word, int word_id) { int node_id = 0; for (int i = 0; i < (int)word.size(); i++) { int c = (int)(word[i] - base); int &next_id = nodes[node_id].next[c]; if (next_id == -1) { // 次の頂点が存在しなければ追加 next_id = (int)nodes.size(); nodes.push_back(Node(c)); } ++nodes[node_id].common; node_id = next_id; } ++nodes[node_id].common; nodes[node_id].accept.push_back(word_id); } void insert(const string &word) { insert(word, nodes[0].common); } // 単語とprefixの検索 bool search(const string &word, bool prefix = false) { int node_id = 0; for (int i = 0; i < (int)word.size(); i++) { int c = (int)(word[i] - base); int &next_id = nodes[node_id].next[c]; if (next_id == -1) { // 次の頂点が存在しなければ終了 return false; } node_id = next_id; } return (prefix) ? true : nodes[node_id].accept.size() > 0; } // prefix を持つ単語が存在するかの検索 bool start_with(const string &prefix) { return search(prefix, true); } // 挿入した単語の数 int count() const { return (nodes[0].common); } // Trie木のノード数 int size() const { return ((int)nodes.size()); } }; // union by size + path having class UnionFind { public: vector <ll> par; // 各元の親を表す配列 vector <ll> siz; // 素集合のサイズを表す配列(1 で初期化) // Constructor UnionFind(ll sz_): par(sz_), siz(sz_, 1LL) { for (ll i = 0; i < sz_; ++i) par[i] = i; // 初期では親は自分自身 } void init(ll sz_) { par.resize(sz_); siz.assign(sz_, 1LL); // resize だとなぜか初期化されなかった for (ll i = 0; i < sz_; ++i) par[i] = i; // 初期では親は自分自身 } // Member Function // Find ll root(ll x) { // 根の検索 while (par[x] != x) { x = par[x] = par[par[x]]; // x の親の親を x の親とする } return x; } // Union(Unite, Merge) bool merge(ll x, ll y) { x = root(x); y = root(y); if (x == y) return false; // merge technique(データ構造をマージするテク.小を大にくっつける) if (siz[x] < siz[y]) swap(x, y); siz[x] += siz[y]; par[y] = x; return true; } bool issame(ll x, ll y) { // 連結判定 return root(x) == root(y); } ll size(ll x) { // 素集合のサイズ return siz[root(x)]; } }; // 0-indexed parmutation only vvl cycle_partition(const vl &p){ ll n=p.size(); vvl ret; vector<bool> check(n,false); rep(i,n)if(!check[p[i]]){ vl v; ll pos=p[i]; v.pb(i); check[i]=true; while(pos!=i){ v.pb(pos); check[pos]=true; pos=p[pos]; } ret.pb(v); } return ret; } //Manachar 修理中 // vl Manachar(string S){ // ll c=0,n=S.size(); // vl R(n,1); // for(ll i=0;i<n;i++){ // ll l=c-(i-c); // if(i+R[l]<c+R[c]){ // R[i]=R[l]; // }else{ // ll j=c+R[c]-i; // while(i-j>=0 && i+j<n && S[i-j] == S[i+j])j++; // R[i]=j; // c=i; // } // } // return R; // } template <typename T> T pow(T a, long long n, T e = 1) { T ret = e; while (n) { if (n & 1) ret *= a; a *= a; n >>= 1; } return ret; } template <int mod> struct ModInt { int x; ModInt() : x(0) {} ModInt(long long x_) { if ((x = x_ % mod + mod) >= mod) x -= mod; } ModInt& operator+=(ModInt rhs) { if ((x += rhs.x) >= mod) x -= mod; return *this; } ModInt& operator-=(ModInt rhs) { if ((x -= rhs.x) < 0) x += mod; return *this; } ModInt& operator*=(ModInt rhs) { x = (unsigned long long)x * rhs.x % mod; return *this; } ModInt& operator/=(ModInt rhs) { x = (unsigned long long)x * rhs.inv().x % mod; return *this; } ModInt operator-() const { return -x < 0 ? mod - x : -x; } ModInt operator+(ModInt rhs) const { return ModInt(*this) += rhs; } ModInt operator-(ModInt rhs) const { return ModInt(*this) -= rhs; } ModInt operator*(ModInt rhs) const { return ModInt(*this) *= rhs; } ModInt operator/(ModInt rhs) const { return ModInt(*this) /= rhs; } bool operator==(ModInt rhs) const { return x == rhs.x; } bool operator!=(ModInt rhs) const { return x != rhs.x; } ModInt inv() const { return pow(*this, mod - 2); } friend ostream& operator<<(ostream& s, ModInt<mod> a) { s << a.x; return s; } friend istream& operator>>(istream& s, ModInt<mod>& a) { s >> a.x; return s; } }; using mint = ModInt<MOD>; /* RMQ:[0,n-1] について、区間ごとの最小値を管理する構造体 update(a,b,x): 区間[a,b) の要素を x に更新。O(log(n)) query(a,b): [a,b) での最小の要素を取得。O(log(n)) */ template <typename T> struct RMQ { //const T INF = numeric_limits<T>::max(); int n; vector<T> dat, lazy; RMQ(int n_) : n(), dat(n_ * 4, INF), lazy(n_ * 4, INF) { int x = 1; while (n_ > x) x *= 2; n = x; } /* lazy eval */ void eval(int k) { if (lazy[k] == INF) return; // 更新するものが無ければ終了 if (k < n - 1) { // 葉でなければ子に伝搬 lazy[k * 2 + 1] = lazy[k]; lazy[k * 2 + 2] = lazy[k]; } // 自身を更新 dat[k] = lazy[k]; lazy[k] = INF; } void update(int a, int b, T x, int k, int l, int r) { eval(k); if (a <= l && r <= b) { // 完全に内側の時 lazy[k] = x; eval(k); } else if (a < r && l < b) { // 一部区間が被る時 update(a, b, x, k * 2 + 1, l, (l + r) / 2); // 左の子 update(a, b, x, k * 2 + 2, (l + r) / 2, r); // 右の子 dat[k] = min(dat[k * 2 + 1], dat[k * 2 + 2]); } } void update(int a, int b, T x) { update(a, b, x, 0, 0, n); } T query_sub(int a, int b, int k, int l, int r) { eval(k); if (r <= a || b <= l) { // 完全に外側の時 return INF; } else if (a <= l && r <= b) { // 完全に内側の時 return dat[k]; } else { // 一部区間が被る時 T vl = query_sub(a, b, k * 2 + 1, l, (l + r) / 2); T vr = query_sub(a, b, k * 2 + 2, (l + r) / 2, r); return min(vl, vr); } } T query(int a, int b) { return query_sub(a, b, 0, 0, n); } /* debug */ inline T operator[](int a) { return query(a, a + 1); } void print() { for (int i = 0; i < 2 * n - 1; ++i) { cout << (*this)[i]; if (i != n) cout << ","; } cout << endl; } }; // aよりもbが大きいならばaをbで更新する // (更新されたならばtrueを返す) template <typename T> bool chmax(T &a, const T& b) { if (a < b) { a = b; // aをbで更新 return true; } return false; } // aよりもbが小さいならばaをbで更新する // (更新されたならばtrueを返す) template <typename T> bool chmin(T &a, const T& b) { if (a > b) { a = b; // aをbで更新 return true; } return false; } /* BIT: RAQ対応BIT 初期値は a_1 = a_2 = ... = a_n = 0 1-Indexedに注意!!!!!!!! ・add(l,r,x): [l,r) に x を加算する ・sum(i): a_1 + a_2 + ... + a_i を計算する query(l,r)で[l,r)の総和出力 計算量は全て O(logn) */ template <typename T> struct BIT { int n; // 要素数 vector<T> bit[2]; // データの格納先 BIT(int n_) { init(n_); } void init(int n_) { n = n_ + 1; for (int p = 0; p < 2; p++) bit[p].assign(n, 0); } void add_sub(int p, int i, T x) { for (int idx = i; idx < n; idx += (idx & -idx)) { bit[p][idx] += x; } } void add(int l, int r, T x) { // [l,r) に加算 add_sub(0, l, -x * (l - 1)); add_sub(0, r, x * (r - 1)); add_sub(1, l, x); add_sub(1, r, -x); } T sum_sub(int p, int i) { T s(0); for (int idx = i; idx > 0; idx -= (idx & -idx)) { s += bit[p][idx]; } return s; } T sum(int i) { return sum_sub(0, i) + sum_sub(1, i) * i; } // [l,r) の区間和を取得 T query(int l, int r) { return sum(r - 1) - sum(l - 1); } }; /*struct Edge { long long to; }; using Graph = vector<vector<Edge>>; */ /* LCA(G, root): 木 G に対する根を root として Lowest Common Ancestor を求める構造体 query(u,v): u と v の LCA を求める。計算量 O(logn) 前処理: O(nlogn)時間, O(nlogn)空間 */ /*struct LCA { vector<vector<int>> parent; // parent[k][u]:= u の 2^k 先の親 vector<int> dist; // root からの距離 LCA(const Graph &G, int root = 0) { init(G, root); } // 初期化 void init(const Graph &G, int root = 0) { int V = G.size(); int K = 1; while ((1 << K) < V) K++; parent.assign(K, vector<int>(V, -1)); dist.assign(V, -1); dfs(G, root, -1, 0); for (int k = 0; k + 1 < K; k++) { for (int v = 0; v < V; v++) { if (parent[k][v] < 0) { parent[k + 1][v] = -1; } else { parent[k + 1][v] = parent[k][parent[k][v]]; } } } } // 根からの距離と1つ先の頂点を求める void dfs(const Graph &G, int v, int p, int d) { parent[0][v] = p; dist[v] = d; for (auto e : G[v]) { if (e.to != p) dfs(G, e.to, v, d + 1); } } int query(int u, int v) { if (dist[u] < dist[v]) swap(u, v); // u の方が深いとする int K = parent.size(); // LCA までの距離を同じにする for (int k = 0; k < K; k++) { if ((dist[u] - dist[v]) >> k & 1) { u = parent[k][u]; } } // 二分探索で LCA を求める if (u == v) return u; for (int k = K - 1; k >= 0; k--) { if (parent[k][u] != parent[k][v]) { u = parent[k][u]; v = parent[k][v]; } } return parent[0][u]; } int get_dist(int u, int v) { return dist[u] + dist[v] - 2 * dist[query(u, v)]; } bool is_on_path(int u, int v, int a) { return get_dist(u, a) + get_dist(a, v) == get_dist(u, v); } }; */ static const int MAX_SIZE = 1 << 17; //segment tree のサイズ。この実装では2べきにする必要がある。 2^17 ≒ 1.3 * 10^5 typedef long long Int; Int segMin[2 * MAX_SIZE - 1], segAdd[2 * MAX_SIZE - 1]; //区間[a, b)に値xを加算する. void add(int a, int b, Int x, int k = 0, int l = 0, int r = MAX_SIZE) { if (r <= a || b <= l) return; //もし交差しない区間であれば終える. if (a <= l && r <= b){ //もし今みている区間[l, r)が[a, b)に完全に内包されていれば segAdd[k] += x; //区間[l, r)にkを加算する. return; } add(a, b, x, k * 2 + 1, l, (l + r) / 2); //子の区間に(必要があれば)xを加算する. add(a, b, x, k * 2 + 2, (l + r) / 2, r); //〃 //親の区間の最小値は, 子の区間の最小値 + 自分に一様に加算されている値 である.一様に加算される値は更新しなくて良い. segMin[k] = min(segMin[k * 2 + 1] + segAdd[k * 2 + 1], segMin[k * 2 + 2] + segAdd[k * 2 + 2]); } Int getMin(int a, int b, int k = 0, int l = 0, int r = MAX_SIZE) { if (r <= a || b <= l) return (LLONG_MAX); if (a <= l && r <= b) return (segMin[k] + segAdd[k]); //完全に内包されていれば,その区間の最小値を返す. Int left = getMin(a, b, k * 2 + 1, l, (l + r) / 2); //子の区間の最小値を求める. Int right = getMin(a, b, k * 2 + 2, (l + r) / 2, r); //子の区間の最小値を求める return (min(left, right) + segAdd[k]); //親の区間の最小値は, 子の区間の最小値 + 自分に一様に加算されている値 である (大切なので2回書きました!!) } int main(){ ios::sync_with_stdio(false); std::cin.tie(nullptr); cout << fixed << setprecision(10); /*--------------------------------*/ ll n;cin>>n; vl a(n); rep(i,n){ cin>>a[i]; add(i+1,i+2,a[i]); } ll q;cin>>q; rep(i,q){ ll k,l,r,c;cin>>k>>l>>r>>c; if(k==1){ add(l,r+1,c); } else cout<<getMin(l,r+1)<<endl; } }