「JOISC 2019 Day2」两道料理(差分+线段树)

https://loj.ac/problem/3034

\(sa\)表示a的前缀和,sb表示b的前缀和。

\(f[i][j]\)表示n个中的前i个、m个中的前j个完成了,最大的分数和。

如果把第一维去掉,\(f[i]\)是由\(f[i-1]\)加上一些修改得到。

那么就是先:

1.\(f[j]+=a[i](sa[i]+sb[j]<=s[i])\)

满足条件的j显然是一个前缀。

2.\(f[j]=max(f[j],f[i]-v[i]+v[j])(i<j)\)

其中\(v\)是满足\(sb[i]+sa[j]<=t[i]\)\(q[i]\)的前缀和。

对于第二个转移,考虑设\(g[j]=f[j]-v[j]\),不难发现,第二个转移在\(g\)上就是一个前缀max。

那么变成在\(g\)上操作:
1.\(g[j]+=a[i](sa[i]+sb[j]<=s[i])\)

扫描二维码关注公众号,回复: 9628693 查看本文章

还是给\(g\)的前缀区间加上一个数。

2.由于\(a[i]\)的加入,会有一些新的\(q[j]\)从v中剔除,v是前缀和,这个对v相当于后缀减,由于这个操作不会改变\(f\),所以相当于g的后缀加。

3.对g取前缀max。

注意1、2的操作都是区间加。

考虑如何维护?先把1、2的区间加离散成若干个极长小区间,显然不管怎么操作,每一个小区间的相对大小不变,也就是依然满足非递减关系,只需要考虑小区间之间的前缀max。

所以,左往右这样更新过去:

比如有相邻的小区间\([lx,rx]\)\(ly,ry\),若\([lx,rx]\)的最大值大于\([ly,ry]\)的开头,那么找到\([ly,ry]\)开头相同的连续一段数,对这个进行区间加,以此类推。

势能分析后复杂度是对的。

Code

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 1e6 + 5;

int n, m;
ll a[N], lima[N], p[N], sa[N];
ll b[N], limb[N], q[N], sb[N], he[N];

void Init() {
    scanf("%d %d", &n, &m);
    fo(i, 1, n) scanf("%lld %lld %lld", &a[i], &lima[i], &p[i]), sa[i] = sa[i - 1] + a[i];
    fo(i, 1, m) scanf("%lld %lld %lld", &b[i], &limb[i], &q[i]), sb[i] = sb[i - 1] + b[i];
}

#define i0 i + i
#define i1 i + i + 1
ll t[N * 4], lz[N * 4]; int g[N * 4];
ll pl, pr, px;
void jia(int i, ll x) {
    t[i] += x, lz[i] += x;
}
void down(int i) {
    if(lz[i]) {
        jia(i0, lz[i]);
        jia(i1, lz[i]);
        lz[i] = 0;
    }
}
void add(int i, int x, int y) {
    if(y < pl || x > pr) return;
    if(x >= pl && y <= pr) { jia(i, px); return;}
    int m = x + y >> 1; down(i);
    add(i0, x, m); add(i1, m + 1, y);
    t[i] = max(t[i0], t[i1]);
    g[i] = (g[i0] & g[i1] & (t[i0] == t[i1]));
}
ll getv(int i, int x, int y, int l) {
    if(g[i]) return t[i];
    int m = x + y >> 1; down(i);
    return l <= m ? getv(i0, x, m, l) : getv(i1, m + 1, y, l);
}
int py, pz; ll pk;
void ft(int i, int x, int y) {
    if(y < pl || x > pr || pz) return;
    if(x >= pl && y <= pr && g[i]) {
        if(t[i] != pk) { pz = 1; return;}
        if(g[i]) { py = y; return;}
    }
    int m = x + y >> 1; down(i);
    ft(i0, x, m);
    ft(i1, m + 1, y);
}

int d[N]; ll h[N];
int cmpd(int x, int y) {
    return h[x] < h[y];
}

int w[N * 8], w0;

void cha(int x, int y, ll z) {
    if(x > y) return;
    pl = x, pr = y, px = z;
    add(1, 0, m);
    w[++ w0] = y; if(x) w[++ w0] = x - 1;
}

void dg(ll v, int x, int y) {
    while(x <= y) {
        ll v2 = getv(1, 0, m, x);
        if(v <= v2) return;
        pl = x, pr = y; py = 0, pz = 0; pk = v2;
        ft(1, 0, m);
        pl = x, pr = py; px = v - v2;
        add(1, 0, m);
        x = py + 1;
    }
}

void premax() {
    sort(w + 1, w + w0 + 1);
    w0 = unique(w + 1, w + w0 + 1) - (w + 1);
    fo(i, 1, w0) dg(getv(1, 0, m, w[i]), w[i] + 1, m);
}

void work() {
    fo(i, 1, m * 4) g[i] = 1;
    fo(i, 1, m) {
        h[i] = limb[i] - sb[i];
        d[i] = i;
    }
    sort(d + 1, d + m + 1, cmpd);
    int l = 1;
    while(l <= m && h[d[l]] < 0) l ++;
    fo(i, 1, n) {
        w0 = 0;
        int as = -1;
        for(int l = 0, r = m; l <= r; ) {
            int mi = l + r >> 1;
            if(sb[mi] <= lima[i] - sa[i]) as = mi, l = mi + 1; else r = mi - 1;
        }
        cha(0, as, p[i]);
        while(l <= m && h[d[l]] < sa[i]) {
            cha(d[l], m, q[d[l]]);
            l ++;
        }
        premax();
    }
    ll ans = getv(1, 0, m, m);
    while(l <= m) ans += q[d[l ++]];
    pp("%lld\n", ans);
}

int main() {
    Init();
    work();
}

猜你喜欢

转载自www.cnblogs.com/coldchair/p/12431331.html