제출 #1347285

#제출 시각아이디문제언어결과실행 시간메모리
1347285penguin133Legendary Dango Eater (JOI26_dango)C++20
100 / 100
1179 ms375920 KiB
// Source: https://usaco.guide/general/io

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define int long long
ll n, q, k;
ll a[500005], r[500005], pref[500005], cry[500005], pref2[500005];
ll par[20][500005], sm[20][500005];

struct node{
    int s, e, m, val;
    node *l, *r;
    node (int _s, int _e){
        s = _s, e = _e, m = (s + e) >> 1;
        val = (ll)1e18;
        l = r = nullptr;
    }

    void mc() {
        if (s == e || l != nullptr) return;
        l = new node(s, m);
        r = new node(m + 1, e);
    }

    void upd (ll p, int v) {
        if (s == e) {
            val = min(val, v);
            return;
        }
        mc();
        if (p <= m) l->upd(p, v);
        else r->upd(p, v);
        val = min(l->val, r->val);
    }

    ll qry(ll a, ll b) {
        if ((a == s && b == e) || l == nullptr) return val;
        else if (b <= m) return l->qry(a, b);
        else if (a > m) return r->qry(a, b);
        else return min(l->qry(a, m), r->qry(m+1, b));
    }

    ll find(ll x, ll a) {
        //cerr << s << ' ' << e << ' ' << x << ' ' << a << ' ' << val << '\n';
        if (s == e) return val <= x ? s : 1e6;
        if (val > x || a > e) return 1e6;
        mc();
        if (a > m) return r->find(x, a);
        ll res = l->find(x, a);
        if (res != 1e6) return res;
        return r->find(x, m + 1);
    }

}*root;

int32_t main() {
    ios::sync_with_stdio(0); cin.tie(0);
	cin >> n >> q >> k;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) {
        pref[i] = pref[i - 1] + a[i] * (i&1?1:-1), r[i] = 1e6;
        pref2[i] = pref2[i - 1] + (i&1?a[i]%k:-a[i]);
        cry[i] = cry[i - 1] + a[i]/k * (i&1);
    }
    vector <pair <ll, int> > up, qu;
    for (int i = 1; i <= n; i += 2) {
        up.push_back({(pref[i] % k + k) % k, i});
        qu.push_back({(pref[i - 1] % k + k) % k, i});
    }
    sort(up.begin(), up.end());
    sort(qu.begin(), qu.end());
    int lst = (n & 1 ? n + 1 : n);
    /*
    vector <ll> disc;
    for (int i = 1; i <= n; i += 2) {
        disc.push_back(pref[i - 1] % k);
        disc.push_back(pref[i] % k);
        disc.push_back(pref[i] % k - 1);
        disc.push_back(pref[i]%k - a[i + 1] % k);
        disc.push_back(pref[i] % k - a[i + 1] % k + k);
    }
    sort(disc.begin(), disc.end());
    disc.erase(unique(disc.begin(), disc.end()), disc.end());
    */
    /*
    for (int i = (n%2?n:n-1); i >= 1; i -= 2) {
        //find first j >= i where it is not good to extend to j + 1
        //basically (a[i] - a[i + 1] + a[i + 2] - a[i + 3] + ... + a[j])%k < a[j + 1]
        //so it must be positive, and if pref[i] = a[1] - a[2] + a[3] - ... + a[i],
        //then (pref[j] - pref[i - 1]) % k < a[j + 1] % k
        //if pref[j] % k >= pref[i - 1] % k, then 
        //pref[j]%k - pref[i - 1]%k < a[j + 1]%k --> pref[i - 1] % k > pref[j] % k - a[j + 1] % k
        //otherwise, pref[j]%k - pref[i - 1]%k + k < a[j + 1] % k --> pref[i - 1] % k > pref[j] % k - a[j + 1] % k + k
        if (a[i + 1] >= k) lst = i + 1;

    }
    */
    //root = new node(0, (int)disc.size() + 5);
    root = new node(1, n);
    int ptr = 0;
    for (auto [val, i] : qu) {
        while (ptr < (int)up.size() && up[ptr].first < val) {
            //ll qv = lower_bound(disc.begin(), disc.end(), up[ptr].first - a[up[ptr].second + 1] % k + k) - disc.begin() + 1;
            //root->upd(qv, up[ptr].second);
            root->upd(up[ptr].second, up[ptr].first - a[up[ptr].second + 1] + k);
            ptr++;
        }
        //ll qv = lower_bound(disc.begin(), disc.end(), val - 1) - disc.begin() + 1;
        //r[i] = min(r[i], root->qry(0, qv));
        r[i] = min(r[i], root->find(val, i));
    }
    //cerr << "hi\n";
    //for (int i = 1; i <= n; i++) cerr << r[i] << ' ';
    //cerr << '\n';
    reverse(up.begin(), up.end());
    reverse(qu.begin(), qu.end());
    //root = new node(0, (int)disc.size() + 5);
    root = new node(1, n);
    ptr = 0;
    for (auto [val, i] : qu) {
        while (ptr < (int)up.size() && up[ptr].first >= val) {
            //ll qv = lower_bound(disc.begin(), disc.end(), up[ptr].first - a[up[ptr].second + 1] % k) - disc.begin() + 1;
            //root->upd(qv, up[ptr].second);
            //cerr << "UPDATE" << ' ' << up[ptr].second << ' ' << up[ptr].first - a[up[ptr].second + 1] << '\n';
            root->upd(up[ptr].second, up[ptr].first - a[up[ptr].second + 1]);
            ptr++;
        }
        //ll qv = lower_bound(disc.begin(), disc.end(), val - 1) - disc.begin() + 1;
        //r[i] = min(r[i], root->qry(0, qv));
        //cerr << "QUERY " << val << ' ' << i << '\n';
        r[i] = min(r[i], root->find(val, i));
    }
    root = new node(0, n);
    //cerr << "hi\n";
    for (int i = n; i >= 1; i--) {
        if (i % 2 == 0 && a[i] >= k) lst = i;
        r[i] = min(r[i], (ll)lst - 1);
        if (i % 2 == 0) root->upd(i, pref2[i]);
        if (i & 1) r[i] = min(r[i], root->find(pref2[i - 1], i) - 1);
    }

    //i->r[i]
    for (int i = 1; i <= n; i += 2) {
        par[0][i] = r[i] + 2;
        sm[0][i] = (pref[r[i]] - pref[i - 1]) / k;
    }
    for (int i = 2; i <= n; i += 2) par[0][i] = n + 1;
    par[0][n + 1] = par[0][0] = n + 1;
    for (int i = 1; i <= 19; i++) {
        for (int j = 1; j <= n; j++) {
            par[i][j] = par[i - 1][par[i - 1][j]];
            sm[i][j] = sm[i - 1][j] + sm[i - 1][par[i - 1][j]];
        }
    }
    //for (int i = 1; i <= n; i++) cerr << r[i] << ' ';
    //cerr << '\n';
    
    //why is this so hard
    while (q--) {
        int l, r1; cin >> l >> r1;
        int cur = (l & 1 ? l : l + 1);
        r1 = (r1 & 1 ? r1 : r1 - 1);
        ll ans = 0;
        /*
        while (cur <= r1) {
            int tmp = r[cur];
            if (tmp > r1) {
                int rb = (r1 & 1 ? r1 : r1 - 1);
                ans += (pref[rb] - pref[cur - 1]) / k;
                break;
            }
            else {
                ans += (pref[tmp] - pref[cur - 1]) / k;
                cur = tmp + 2;
            }
        }
        */
        
        for (int i = 19; i >= 0; i--) {
            if (par[i][cur] <= r1 && par[i][cur] >= 1) {
                ans += sm[i][cur];
                cur = par[i][cur];
            }
        }
        if (cur <= r1) ans += (pref[r1] - pref[cur - 1]) / k;
        
        cout << ans << '\n';
        /*
        ll ext = 0, cur = 0;
        for (int i = l; i <= r; i++) {
            if (i & 1) {
                cur += (a[i] + ext) / k;
                ext = (a[i] + ext) % k;
            }
            else {
                ext = max(0ll, ext - a[i]);
            }
        }
        cout << cur << '\n';
        */
    }
}
#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...
#Verdict Execution timeMemoryGrader output
Fetching results...