結果
| 問題 |
No.1234 典型RMQ
|
| コンテスト | |
| ユーザー |
re_re0101
|
| 提出日時 | 2020-09-18 21:55:10 |
| 言語 | C++14 (gcc 13.3.0 + boost 1.87.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 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 27 |
ソースコード
#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;
}
}
re_re0101