Submission #1152084

#TimeUsernameProblemLanguageResultExecution timeMemory
1152084Zero_OPFish 2 (JOI22_fish2)C++20
100 / 100
1707 ms20588 KiB
//https://codeforces.com/blog/entry/101003?#comment-898608
#include <bits/stdc++.h>

using namespace std;

//loops (warning : long long)
#define FOR(i, l, r) for(int i = (l); i < (r); ++i)
#define ROF(i, r, l) for(int i = (r - 1); i >= l; --i)

//pairs, tuples
#define mp make_pair
#define mt make_tuple
#define ff first
#define ss second

//vectors
#define all(v) begin(v), end(v)
#define rall(v) rbegin(v), rend(v)
#define pb push_back
#define eb emplace_back
#define sum_of(v) accumulate(all(v), 0ll)
#define sz(v) (int)v.size()
#define compact(v) v.erase(unique(all(v)), end(v))

//binary search
#define lwb lower_bound
#define upb upper_bound

//other stuffs
#define dbg(x) "[" #x " = " << (x) << "]"
#define file(task) if(fopen(task".inp", "r")){ freopen(task".inp", "r", stdin); freopen(task".out", "w", stdout); }

template<typename T>
bool minimize(T& a, const T& b){
    if(a > b) return a = b, true;
    return false;
}

template<typename T>
bool maximize(T& a, const T& b){
    if(a < b) return a = b, true;
    return false;
}

using ll = long long;
using ull = unsigned long long;
using ld = long double;
using db = double;
using pi = pair<int, int>;
using pl = pair<ll, ll>;

using vi = vector<int>;
using vb = vector<bool>;
using vl = vector<ll>;
using vpi = vector<pi>;
using vpl = vector<pl>;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());

const int MAX = 1e5 + 5;
const ll inf = 1e15 + 100;

int N, Q;
ll a[MAX];
ll pref[MAX];

struct FenwickTree{
    const int offset = 0;
    vl bit;
    void init(int n){
        bit = vl(n+1, 0);
    }

    void update(int i, ll v){
        i += offset;
        for(; i < sz(bit); i += i & (-i)) bit[i] += v;
    }

    ll queryPrefix(int i){
        ll sum = 0;
        i += offset;
        for(; i > 0; i -= i & (-i)) sum += bit[i];
        return sum;
    }

    ll query(int l, int r){
        return queryPrefix(r) - queryPrefix(l-1);
    }
} Fenwick;

struct SegmentTreeMinCount{
    struct Node{
        int mn, cnt;
        Node() : mn(0), cnt(0) {}
        Node(int mn, int cnt) : mn(mn), cnt(cnt) {}

        friend Node operator + (const Node& a, const Node& b){
            if(a.mn == b.mn) return Node(a.mn, a.cnt + b.cnt);
            else return (a.mn < b.mn ? a : b);
        }
    };
    
    vector<Node> st;
    vi lazy;
    int N;
    SegmentTreeMinCount(int n) : N(n), st(n << 2), lazy(n << 2) {}
    
    void apply(int id, int delta){
        st[id].mn += delta;
        lazy[id] += delta;
    }

    void down(int id){
        if(lazy[id] != 0){
            apply(id << 1, lazy[id]);
            apply(id << 1 | 1, lazy[id]);
            lazy[id] = 0;
        }
    }
    
    void build(int id, int l, int r){
        st[id].cnt = r - l + 1;
        if(l < r){
            int mid = l + r >> 1;
            build(id << 1, l, mid);
            build(id << 1 | 1, mid + 1, r);
        }
    }

    void update(int id, int l, int r, int u, int v, int delta){
        if(u <= l && r <= v) apply(id, delta);
        else{
            int mid = l + r >> 1;
            down(id);
            if(u <= mid) update(id << 1, l, mid, u, v, delta);
            if(mid < v)  update(id << 1 | 1, mid + 1, r, u, v, delta);
            st[id] = st[id << 1] + st[id << 1 | 1];
        }
    }

    Node query(int id, int l, int r, int u, int v){
        if(u <= l && r <= v) return st[id];
        int mid = l + r >> 1;
        down(id);
        if(u > mid)  return query(id << 1 | 1, mid + 1, r, u, v);
        if(mid >= v) return query(id << 1, l, mid, u, v);
        return query(id << 1, l, mid, u, v) + query(id << 1 | 1, mid + 1, r, u, v);
    }

    int count_min(int l, int r){
        Node cur = query(1, 1, N, l, r);
        return cur.cnt;
    }

    void debug(int id, int l, int r){
        if(l == r){
            cout << st[id].mn << ' ';
            if(r == N) cout << '\n';
        } else{
            int mid = l + r >> 1;
            down(id);
            debug(id << 1, l, mid);
            debug(id << 1 | 1, mid + 1, r);
        }
    }
} MinCount(0);

struct SegmentTreeRightEndpoint{
    vl st, lazy;
    void init(int n){
        st.resize(4 * n, 0);
        lazy.resize(4 * n, 0);
    }

    void apply(int id, ll val){
        st[id] += val;
        lazy[id] += val;
    }

    void down(int id){
        if(lazy[id] != 0){
            apply(id << 1, lazy[id]);
            apply(id << 1 | 1, lazy[id]);
            lazy[id] = 0;
        }
    }

    void build(int id, int l, int r){
        if(l == r){
            //pref[j-1] - pref[i] < a[j]
            //<=> pref[j-1] - a[j] < pref[i]
            st[id] = pref[l-1] - a[l];
        } else{
            int mid = l + r >> 1;
            build(id << 1, l, mid);
            build(id << 1 | 1, mid + 1, r);
            st[id] = min(st[id << 1], st[id << 1 | 1]);
        }
    }

    void update(int id, int l, int r, int u, int v, ll delta){
        if(u <= l && r <= v) apply(id, delta);
        else{
            int mid = l + r >> 1;
            down(id);
            if(u <= mid) update(id << 1, l, mid, u, v, delta);
            if(mid < v)  update(id << 1 | 1, mid + 1, r, u, v, delta);
            st[id] = min(st[id << 1], st[id << 1 | 1]);
        }
    }

    int find_next(int id, int l, int r, int u, int v, ll s){
        if(v < l || u > r) return -1;
        if(st[id] >= s) return -1;

        if(u <= l && r <= v){
            while(l < r){
                int mid = l + r >> 1;
                down(id);
                if(st[id << 1] < s) id = id << 1, r = mid;
                else id = id << 1 | 1, l = mid + 1;
            }
            return l;
        } else{
            int mid = l + r >> 1;
            down(id);
            int go = find_next(id << 1, l, mid, u, v, s);
            if(go != -1) return go;
            return find_next(id << 1 | 1, mid + 1, r, u, v, s);
        }
    }

    int find_next(int l, int r, ll s){
        if(l > r) return N+1;
        int R = find_next(1, 1, N, l, r, s);
        if(R == -1) return N+1;
        return R;
    }
} TreeRightEndpoint;

struct SegmentTreeLeftEndpoint{ 
    vl st, lazy;
    void init(int n){
        st.resize(4 * n, 0);
        lazy.resize(4 * n, 0);
    }

    void apply(int id, ll val){
        st[id] += val;
        lazy[id] += val;
    }

    void down(int id){
        if(lazy[id] != 0){
            apply(id << 1, lazy[id]);
            apply(id << 1 | 1, lazy[id]);
            lazy[id] = 0;
        }
    }

    void build(int id, int l, int r){
        if(l == r){
            //pref[j-1] - pref[i] < a[i]
            //pref[j] < pref[i] + a[i]
            st[id] = pref[l] + a[l];
        } else{
            int mid = l + r >> 1;
            build(id << 1, l, mid);
            build(id << 1 | 1, mid + 1, r);
            st[id] = max(st[id << 1], st[id << 1 | 1]);
        }
    }

    void update(int id, int l, int r, int u, int v, ll delta){
        if(u <= l && r <= v) apply(id, delta);
        else{
            int mid = l + r >> 1;
            down(id);
            if(u <= mid) update(id << 1, l, mid, u, v, delta);
            if(mid < v)  update(id << 1 | 1, mid + 1, r, u, v, delta);
            st[id] = max(st[id << 1], st[id << 1 | 1]);
        }
    }

    int find_next(int id, int l, int r, int u, int v, ll s){
        if(v < l || u > r) return -1;
        if(st[id] <= s) return -1;
        
        if(u <= l && r <= v){
            while(l < r){
                int mid = l + r >> 1;
                down(id);
                if(st[id << 1 | 1] > s) id = id << 1 | 1, l = mid + 1;
                else  id = id << 1, r = mid;
            }
            return l; 
        } else{
            int mid = l + r >> 1;
            down(id);
            int go = find_next(id << 1 | 1, mid + 1, r, u, v, s);
            if(go != -1) return go;
            return find_next(id << 1, l, mid, u, v, s);
        }
    }

    int find_next(int l, int r, ll s){
        if(l > r) return 0;
        int L = find_next(1, 1, N, l, r, s);
        if(L == -1) L = 0;
        return L;
    }
} TreeLeftEndpoint;

set<pair<int, int>> segs;

void add_segment(int l, int r){
    // cout << "insert : " << l << ' ' << r << '\n';
    // assert(r-l > 1);
    // segs.insert({l, r});
    MinCount.update(1, 1, N, l+1, r-1, +1); 
}

void erase_segment(int l, int r){
    // cout << "erase : " << l << ' ' << r << '\n';
    // assert(r-l > 1);
    // segs.erase({l, r});
    MinCount.update(1, 1, N, l+1, r-1, -1);
}

void debug_segs(){
    for(auto [l, r] : segs) cout << dbg(l) << ' ' << dbg(r) << '\n';
}

bool bad(int l, int r){
    // assert(r-l > 1);
    return min(a[l], a[r]) > Fenwick.query(l+1, r-1);
}

void extract_left_endpoint(int L, vpi& result){
    if(L+2 > N+1) return;
    int R = L+2;
    ll fixed_sum = Fenwick.queryPrefix(L);
    while(true){
        if(bad(L, R)) result.eb(L, R);
        if(R == N+1) break;
        //pref[R-1] - pref[L] < a[R] 
        R = TreeRightEndpoint.find_next(R+1, N, fixed_sum);
    }
}

void extract_right_endpoint(int R, vpi& result){
    if(R-2 < 0) return;
    int L = R-2;
    ll fixed_sum = Fenwick.queryPrefix(R-1);
    while(true){
        if(bad(L, R)) result.eb(L, R);
        if(L == 0) break;
        //pref[R-1] - pref[L] > a[L]
        L = TreeLeftEndpoint.find_next(1, L-1, fixed_sum);
    }
}

void extract_include_point_segments(int i, vpi& result){
    if(i-1 < 0 || i+1 > N+1) return;
    int L = i-1, R = i+1;
    while(true){
        if(bad(L, R)) result.eb(L, R);
        if(L == 0 && R == N+1) break;
        if(a[L] < a[R]) L = TreeLeftEndpoint.find_next(1, L-1, Fenwick.queryPrefix(R-1));
        else R = TreeRightEndpoint.find_next(R+1, N, Fenwick.queryPrefix(L));
    }
}

void update(int i, int x){
    vpi segments;
    extract_left_endpoint(i-1, segments);
    extract_right_endpoint(i-1, segments);
    extract_left_endpoint(i+1, segments);
    extract_right_endpoint(i+1, segments);
    extract_left_endpoint(i, segments);
    extract_right_endpoint(i, segments);
    extract_include_point_segments(i, segments);
    sort(all(segments)); compact(segments);
    for(auto [l, r] : segments) erase_segment(l, r);
    segments.clear();

    Fenwick.update(i, -a[i] + x);
    TreeLeftEndpoint.update(1, 1, N, i, N, -a[i] + x);
    TreeLeftEndpoint.update(1, 1, N, i, i, -a[i] + x);
    if(i < N) TreeRightEndpoint.update(1, 1, N, i+1, N, -a[i] + x);
    TreeRightEndpoint.update(1, 1, N, i, i, +a[i] - x);
    a[i] = x;

    extract_left_endpoint(i-1, segments);
    extract_right_endpoint(i-1, segments);
    extract_left_endpoint(i+1, segments);
    extract_right_endpoint(i+1, segments);
    extract_left_endpoint(i, segments);
    extract_right_endpoint(i, segments);
    extract_include_point_segments(i, segments);
    sort(all(segments)); compact(segments);
    for(auto [l, r] : segments) add_segment(l, r);
}

ll query(int l, int r){
    int R = r;
    while(true){
        int nxt = TreeLeftEndpoint.find_next(1, R-1, Fenwick.queryPrefix(r)); 
        //pref[r] - pref[R-1] < a[R-1] <=> pref[r] < a[R-1] + pref[R-1]
        if(nxt < l) break;
        R = nxt;
    }

    int L = l;
    while(true){
        int nxt = TreeRightEndpoint.find_next(L+1, N, Fenwick.queryPrefix(l-1));
        //pref[L] - pref[l-1] > a[L+1] <=> pref[L] - a[L+1] > pref[l-1]
        if(nxt > r) break;
        L = nxt;
    }

    // cout << dbg(L) << dbg(R) << '\n';
    // MinCount.debug(1, 1, N);
    int result = MinCount.count_min(L, R);
    return result;
}

void testcase(int ntestcase){
    cin >> N;
    a[0] = a[N+1] = inf;

    FOR(i, 1, N+1) {
        cin >> a[i];
        pref[i] = pref[i-1] + a[i];
    }

    Fenwick.init(N);
    TreeLeftEndpoint.init(N);
    TreeRightEndpoint.init(N);
    MinCount = SegmentTreeMinCount(N);

    FOR(i, 1, N+1) Fenwick.update(i, +a[i]);
    TreeLeftEndpoint.build(1, 1, N);
    TreeRightEndpoint.build(1, 1, N);
    MinCount.build(1, 1, N);

    FOR(i, 0, N){
        //finding all j that min(a[i], a[j]) > sum[i+1, j-1]
        //<=> pref[j-1] - pref[i] < min(a[i], a[j])
        vpi cur; 
        extract_left_endpoint(i, cur);
        for(auto [l, r] : cur) add_segment(l, r);       
    }

    // ROF(j, N+2, 2){
    //     //finding all i that min(a[i], a[j]) > sum[i+1, j-1]
    //     //<=> pref[j-1] - pref[i] < min(a[i], a[j])
    //     //<=> pref[j-1] - pref[i] < a[i]
    //     //<=> pref[j-1] < a[i] + pref[i]
    //     int i = j - 2;

    //     while(true){
    //         if(pref[j-1] - pref[i] < min(a[i], a[j])) add_segment(i, j);
    //         if(i < 1) break;
    //         i = TreeLeftEndpoint.find_next(1, i-1, pref[j-1]);
    //     }
    // }

    // sort(all(segments));
    // for(auto [l, r] : segments) cout << l << ' ' << r << '\n';
    
    cin >> Q;
    while(Q--){
        int t;
        cin >> t;
        if(t == 1){
            int x, y;
            cin >> x >> y;
            update(x, y);
            // debug_segs();
            // cout << "_~~_~_~_~__~_~_~_~_~_~_~_~~_~_~_~\n";
        } else{
            int l, r;
            cin >> l >> r;
            // debug_segs();
            cout << query(l, r) << '\n';
            // cout << "_~~_~_~_~__~_~_~_~_~_~_~_~~_~_~_~\n";
        }
    }
}

int main(){
    ios_base::sync_with_stdio(0); cin.tie(0);

#ifdef LOCAL
    freopen("in.txt", "r", stdin);
    freopen("main.out", "w", stdout);
#endif // LOCAL

    int T = 1;
//    cin >> T;
    FOR(i, 0, T) testcase(i);

    return 0;
}

/*

Ý tưởng : 
a[0] = a[N+1] = \infty
Xét tất cả các đoạn [l, r] thỏa :
- min(a[l-1], a[r+1]) > pref[r] - pref[l-1] (*) : Do những vị trí trong [l, r] không thể sống sót

Tính chất của điều kiện (*) : 
- Chỉ có nhiều nhất N đoạn thỏa mãn 
- Với hai đoạn thẳng bất kỳ, chúng chỉ có thể không giao nhau hoặc bao nhau
- Với mỗi vị trí i, số lượng đoạn thẳng [l, r] có thể chứa i nhiều nhất là \log_2(10^9)

Giờ đưa hết các đoạn thẳng thỏa mãn (*) vào tập S để xử lý truy vấn, cập nhật

Cập nhật vị trí i thành giá trị x : 
- Ta chỉ cần quan tâm các đoạn [l, r] có đầu mút là i-1/i/i+1 : 
    + Ta xóa hết các đoạn hiện tại chứa i
    + Ta cập nhật lại các CTDl rồi xử lý lại, bước thêm những đoạn có chứa i
- Dùng một cây BIT xử lý bước tính tổng nhanh

Truy vấn đoạn [l, r] : 
- Ta sẽ tìm vị trí l' và r' : 
    + l' là vị trí trái nhất mà a[l'] > sum[l...l'-1] (nếu không có thì sẽ là l)
    + r' là vị trí phải nhất mà a[r'] > sum[r'+1...r] (nếu không có thì sẽ là r)
- Đếm số vị trí j trong đoạn [l', r'] mà không có đoạn nào thuộc S phủ lên j

*/
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...