CF115E Linear Kingdom Races

做过的原题不会 该反思了。


做法一

考虑 fi 表示考虑前 i 条路能获得的最大价值

那么如果不修这条路 就有 fi=fi1

如果修了这条路 考虑把含 i 的一段前缀即 [j,i] 这段的路都修好
costj,i 表示把 [j,i] 区间的路都修好需要的花费
valj,i 表示把 [j,i] 区间的路都修好能获得的价值
那么就有 fi=max1j<i(fj1costj,i+valj,i)

考虑线段树优化 DP 首先我们把所有路按右端点排序
然后线段树的第 j 个位置表示对于当前的 i 来说 fj1costj,i+valj,i 的值

那么我们考虑右移 i 的对于这棵线段树上每个点的值的影响
首先有 costj,i1costj,i 多出了 i 这个点的花费
其次有 valj,i1valj,i 即对于所有右端点在 i 的比赛 设它的左端点是 l 那么对于所有 jl 都要加上这条路的收益
最后更新 fi 并把它插入 i 位置
这样转移直接查区间 max 即可 总复杂度 O(nlogn)

#include <bits/stdc++.h>
#define ls (k << 1)
#define rs (k << 1 | 1)
#define mid (l + r >> 1)
#define ll long long
#define int long long
using namespace std;

namespace steven24 {

const int N = 2e5 + 0721;
const ll inf = 0x7ffffffffffffff;
int v[N];
ll f[N];
int n, m;

struct node {
	int l, r, p;
	friend bool operator<(node x, node y) {
		return x.r < y.r;
	}
} race[N];

struct tree {
	ll tr[N << 2], lazy[N << 2];
	
	inline void pushup(int k) {
		tr[k] = max(tr[ls], tr[rs]);
	}
	
	void add(int k, int val) {
		tr[k] += val;
		lazy[k] += val;
	}
	
	void pushdown(int k) {
		add(ls, lazy[k]);
		add(rs, lazy[k]);
		lazy[k] = 0;
	}
	
	void modify(int k, int l, int r, int val, int u, int v) {
		if (u <= l && v >= r) {
			add(k, val);
			return;
		}
		if (lazy[k]) pushdown(k);
		if (u <= mid) modify(ls, l, mid, val, u, v);
		if (v > mid) modify(rs, mid + 1, r, val, u, v);
		pushup(k);
	}
	
	void give(int k, int l, int r, int val, int loc) {
		if (l == r) {
			tr[k] = val;
			return;
		}
		if (lazy[k]) pushdown(k);
		if (loc <= mid) give(ls, l, mid, val, loc);
		else give(rs, mid + 1, r, val, loc);
		pushup(k);
	}
	
	ll query(int k, int l, int r, int u, int v) {
		if (u <= l && v >= r) {
			return tr[k];
		}
		if (lazy[k]) pushdown(k);
		ll ret = -inf;
		if (u <= mid) ret = max(ret, query(ls, l, mid, u, v));
		if (v > mid) ret = max(ret, query(rs, mid + 1, r, u, v));
		pushup(k);
		return ret;
	}
} seg;

void dp() {
	int prs = 1;
	for (int i = 1; i <= n; ++i) {
		seg.modify(1, 1, n, -v[i], 1, i);
		while (prs <= m && race[prs].r == i) {
			seg.modify(1, 1, n, race[prs].p, 1, race[prs].l);
			++prs;
		}
		f[i] = max(f[i - 1], seg.query(1, 1, n, 1, i));
		seg.give(1, 0, n, f[i], i + 1);
	}
}

void main() {
	
	scanf("%lld%lld", &n, &m);
	for (int i = 1; i <= n; ++i) scanf("%lld", &v[i]);
	for (int i = 1; i <= m; ++i) scanf("%lld%lld%lld", &race[i].l, &race[i].r, &race[i].p);
	sort(race + 1, race + 1 + m);
	
	dp();
	
	printf("%lld", f[n]);
}
}

signed main() {
	steven24::main();
	return 0;
}
/*
7 4
3 2 3 2 1 2 3
1 2 5
2 3 5
3 5 3
7 7 5
*/

做法二

来自 @KHIN

考虑 fi,j 表示我当前右端点在 i 强制[j,i] 这段区间都修好的最大收益
特别地 定义 fi,i+1 表示考虑前 i 个位置的最大收益

考虑转移 枚举 jifi,j=fi1,jci+valk
其中 k 表示所有右端点为 i 左端点 j 的比赛
对于 fi,i+1fi,i+1=max(max1jifi,j,fi1,i) 代表修/不修 i 这个点

考虑线段树优化 DP 考虑 i1i 的过程
首先大家都要减去 ci
然后考虑每个右端点在 i 的比赛 所有 [l,i] 的点都要加上 valk

所以我们转移的时候先记录 fi1,i
然后更新 f 数组
最后把 fi,i+1 插入

先转移有限制的 再转移无限制的

posted @   Steven24  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示