「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]))

还是给(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("
")
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
", ans);
}

int main() {
	Init();
	work();
}
原文地址:https://www.cnblogs.com/coldchair/p/12431331.html