1. 程式人生 > >[POI 2009]Lyz

[POI 2009]Lyz

是否 cpp pac mat using 線段 pre code sca

Description

題庫鏈接

初始時滑冰俱樂部有 \(1\)\(n\) 號的溜冰鞋各 \(k\) 雙。已知 \(x\) 號腳的人可以穿 \(x\)\(x+d\) 的溜冰鞋。有 \(m\) 次操作,每次包含兩個數 \(r_i\)\(x_i\) 代表來了 \(x_i\)\(r_i\) 號腳的人。 \(x_i\) 為負,則代表走了這麽多人。 對於每次操作,輸出溜冰鞋是否足夠。

\(1\leq n\leq 200,000,1\leq m\leq 500,000\)

Solution

\(\text{Hall}\) 定理以及貪心的思想,容易得到 \(\forall l,r\in[1,n],l\leq r\)

\[\sum_{i=l}^rx_i\leq k(r+d-l+1)\]

移項,得到

\[\sum_{i=l}^r(x_i-k)\leq k\times d\]

顯然只要有 \((x_i-k)\) 最大子段和 \(\leq k\times d\) 。線段樹維護即可。

Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 200000+5;

int n, m, k, d, x, r;
struct node {
    ll mx, l, r, sum;
    node (ll _mx = 0, ll _l = 0, ll _r = 0, ll _sum = 0) {
        mx = _mx, l = _l, r = _r, sum = _sum;
    }
    node operator + (const node &b) const {
        node ans;
        ans.l = max(l, sum+b.l); ans.r = max(b.r, b.sum+r);
        ans.sum = sum+b.sum; ans.mx = max(r+b.l, max(mx, b.mx));
        return ans;
    }
} ;
struct Segment_tree {
#define lr(o) (o<<1)
#define rr(o) (o<<1|1)
    node key[N<<2];
    void build(int o, int l, int r) {
        if (l == r) {key[o] = node(-k, -k, -k, -k); return; }
        int mid = (l+r)>>1;
        build(lr(o), l, mid), build(rr(o), mid+1, r);
        key[o] = key[lr(o)]+key[rr(o)];
    }
    void modify(int o, int l, int r, int loc, int val) {
        if (l == r) {
            ll t = key[o].sum+val;
            key[o] = node(t, t, t, t);
            return;
        }
        int mid = (l+r)>>1;
        if (loc <= mid) modify(lr(o), l, mid, loc, val);
        else modify(rr(o), mid+1, r, loc, val);
        key[o] = key[lr(o)]+key[rr(o)];     
    }
} T;

void work() {
    scanf("%d%d%d%d", &n, &m, &k, &d);
    T.build(1, 1, n);
    while (m--) {
        scanf("%d%d", &r, &x);
        T.modify(1, 1, n, r, x);
        puts(T.key[1].mx <= 1ll*k*d ? "TAK" : "NIE");
    }
}
int main() {work(); return 0; }

[POI 2009]Lyz