题解:CF671E Organizing a Race

题目链接

分析

我们显然应该对于每个 \(l\) 求出其可匹配的最大 \(r\),但注意这个长度不是单调的(即若 \([l,r+1]\) 合法,区间 \([l,r]\) 不一定合法),所以不能二分。

我们可以先思考如何判断一个区间 \([l,r]\) 是否合法。显然可以贪心,\(l \rightarrow r\) 时在必须加油的时候再加,\(r \rightarrow l\) 时则全部加在 \(r\)。

推理

记:

  • \(sg_i=\sum_{j=1}^{i} g_j\)

  • \(sg'_i=\sum_{j=1}^{i} g_j+x_j\)(\(x_j\) 为在 \(j\) 城市放置的礼物个数)

  • \(sw_i=\sum_{j=1}^{i} w_j\)

  • \(c_1\) 为 \(l \rightarrow r\) 时要加的最小礼物数量,有 \(c_1=sg'_{r}-sg_{r}\)。

  • \(c_2\) 为 \(r \rightarrow l\) 时在 \(r\) 要加的最小礼物数量。根据回来时的要求可列出不等式:

\[\begin{aligned} g_r+c_2+sg'_{r-1}-sg'_i &\ge sw_{r-1}-sw_{i-1}\\ c_2 &\ge sw_{r-1}-sg'_{r-1}+sg'_i-sw_{i-1}-g_r\\ c_2 &\ge sw_{r-1}-sg'_r+\max_{i-l}^{r} (sg'_i-sw_{i-1}) \end{aligned}\]

那么显然 \(c_1+c_2 \le k\) 时区间是合法的,即:

\[\begin{aligned} c_1+\max(0,sw_{r-1}-sg'_r+\max_{i=l}^{r} (sg'_i-sw_{i-1})) &\le k\\ \max(c_1,sw_{r-1}-sg_r+\max_{i=l}^{r} (sg'_i-sw_{i-1})) &\le k \end{aligned}\]

设 \(A_i=sg'_i-sw_{i-1},B_i=sw_{i-1}-sg_i,C_i=sg'_{i-1}-sg_{i-1}\),那么区间 \([l,r]\) 合法的条件为:

\[\max(C_r,B_r+\max_{i=l}^{r} A_i) \le k \]

求解

考虑一个区间 \([l,r]\) 在 \(l \rightarrow r\) 时会在什么时候加礼物。记 \(v_i=sg_{i-1}-sw_{i-1}\),根据贪心策略我们只会在 \(v_i>v_{i+1}\) 时在 \(i\) 加 \(val=v_i-v_{i+1}\) 个礼物,即 \(\forall j \in [i,n],A_j \leftarrow val,\forall j \in [i+1,n],C_i \leftarrow val\)。那么我们只需要将 \([l,n]\) 所有要加礼物的地方加礼物,然后按照 \(C_r \le k,B_r + \max_{i-l}^{r} A_i\le k\) 算答案即可,这个显然可以线段树二分。

我们考虑求出 \(nxt_i\) 表示从 \(i\) 出发,初始油量为 \(0\),下一个加礼物的地方(若不用加则 \(nxt_i=n+1\))。显然若按 \(i \rightarrow nxt_i\) 连边,我们就得到了一棵树,那么我们直接在树上 \(dfs\) 即可。具体地,进入一个点 \(x\) 时我们加上它的贡献,即 \(\forall i \in [nxt_x-1,n],A_i \leftarrow v_x-v_{nxt_{x}},\forall i \in [nxt_x,n],C_i \leftarrow v_x-v_{nxt_x}\),离开一个点时删除其贡献即可。

代码
#include <bits/stdc++.h>

#define ll long long

#define eb emplace_back

#define pii pair<int, ll>

#define mp make_pair


using namespace std;

const int maxn = 1e6 + 5;

int n, k, w[maxn], g[maxn], ans = 1;
ll sw[maxn], sg[maxn], v[maxn];
int nxt[maxn];
vector<int> e[maxn];

struct node {
	ll a, b, c;
	int ka;
	
	node operator+ (const node &t) const {
		if(ka == -1) return t;
		node res;
		if(a > t.a) res.a = a, res.ka = ka;
		else res.a = t.a, res.ka = t.ka;
		res.b = min(b, t.b), res.c = min(c, t.c);
		return res;
	}
};

struct SGT {
	node tr[maxn << 2];
	ll tagA[maxn << 2], tagC[maxn << 2];
	
	inline int ls(int p) { return p << 1; }
	inline int rs(int p) { return p << 1 | 1; }
	inline void push_up(int p) { tr[p] = tr[ls(p)] + tr[rs(p)]; }
	
	inline void changeA(int p, ll k) { tr[p].a += k; tagA[p] += k; }
	inline void changeC(int p, ll k) { tr[p].c += k; tagC[p] += k; }
	
	inline void push_down(int p) {
		if(tagA[p]) {
			changeA(ls(p), tagA[p]);
			changeA(rs(p), tagA[p]);
			tagA[p] = 0;
		}
		if(tagC[p]) {
			changeC(ls(p), tagC[p]);
			changeC(rs(p), tagC[p]);
			tagC[p] = 0;
		}
	}
	
	void build(int p, int l, int r) {
		if(l == r) return (void)(tr[p] = {sg[l] - sw[l - 1], sw[l - 1] - sg[l], 0, l});
		int mid = (l + r) >> 1;
		build(ls(p), l, mid), build(rs(p), mid + 1, r);
		push_up(p);
	}
	
	void updateA(int l, int r, int p, int pl, int pr, ll k) {
		if(l <= pl && pr <= r) return changeA(p, k);
		push_down(p);
		int mid = (pl + pr) >> 1;
		if(l <= mid) updateA(l, r, ls(p), pl, mid, k);
		if(mid < r) updateA(l, r, rs(p), mid + 1, pr, k);
		push_up(p);
	}
	
	void updateC(int l, int r, int p, int pl, int pr, ll k) {
		if(l <= pl && pr <= r) return changeC(p, k);
		push_down(p);
		int mid = (pl + pr) >> 1;
		if(l <= mid) updateC(l, r, ls(p), pl, mid, k);
		if(mid < r) updateC(l, r, rs(p), mid + 1, pr, k);
		push_up(p);
	}
	
	node qry(int l, int r, int p, int pl, int pr) {
		if(l <= pl && pr <= r) return tr[p];
		push_down(p);
		int mid = (pl + pr) >> 1;
		node res = {-1, -1, -1, -1};
		if(l <= mid) res = res + qry(l, r, ls(p), pl, mid);
		if(mid < r) res = res + qry(l, r, rs(p), mid + 1, pr);
		return res;
	}
	
	int qryb(int l, int r, int p, int pl, int pr, ll v) {
		if(r < pl || pr < l) return -1;
		if(tr[p].b > v) return -1;
		if(pl == pr) return pl;
		push_down(p);
		int mid = (pl + pr) >> 1;
		if(tr[rs(p)].b <= v) {
			int res = qryb(l, r, rs(p), mid + 1, pr, v);
			if(~res) return res;
		}
		return qryb(l, r, ls(p), pl, mid, v);
	}
	
	int qryc(ll v, int p, int l, int r) {
		if(l == r) return l;
		push_down(p);
		int mid = (l + r) >> 1;
		if(tr[rs(p)].c <= v) return qryc(v, rs(p), mid + 1, r);
		return qryc(v, ls(p), l, mid);
	}
} sgt;

void dfs(int x) {
	if(nxt[x] && nxt[x] <= n) {
		ll val = v[x] - v[nxt[x]];
		sgt.updateA(nxt[x] - 1, n, 1, 1, n, val);
		sgt.updateC(nxt[x], n, 1, 1, n, val);
	}
	int r = sgt.qryc(k, 1, 1, n);
	if(x < r) {
		node A = sgt.qry(x, r - 1, 1, 1, n);
		ans = max(ans, sgt.qryb(A.ka, r, 1, 1, n, k - A.a) - x + 1);
	}
	for(int y : e[x]) dfs(y);
	if(nxt[x] && nxt[x] <= n) {
		ll val = v[nxt[x]] - v[x];
		sgt.updateA(nxt[x] - 1, n, 1, 1, n, val);
		sgt.updateC(nxt[x], n, 1, 1, n, val);
	}
}

int main() {
	scanf("%d%d", &n, &k);
	for(int i = 1; i < n; i++) {
		scanf("%d", &w[i]);
		sw[i] = sw[i - 1] + w[i];
	}
	for(int i = 1; i <= n; i++) {
		scanf("%d", &g[i]);
		sg[i] = sg[i - 1] + g[i];
	}
	stack<int> st;
	for(int i = 1; i <= n; i++) {
		v[i] = sg[i - 1] - sw[i - 1];
		for(; !st.empty() && v[st.top()] > v[i]; st.pop()) nxt[st.top()] = i, e[i].eb(st.top());
		st.push(i);
	}
	for(; !st.empty(); st.pop()) nxt[st.top()] = n + 1, e[n + 1].eb(st.top());
	sgt.build(1, 1, n);
	dfs(n + 1);
	printf("%d\n", ans);
	return 0;
}
posted @ 2026-09-25 08:59  PrinceEvan  阅读(12)  评论(0)    收藏  举报