AT dp W
题目描述
定义一个长度为 \(N\) 的 \(01\) 串 \(S\) 的得分为:
- 对于每个 \(1\le i\le M\),如果存在 \(l_i\le j\le r_i 且 S_j=1\),则将 \(a_i\) 添加到得分中。(\(a_i\) 可能为负数)
找到字符串的最大可能得分。
思路
我们试着将问题反过来:只有在 \([l_i,r_i]\) 中全是 \(0\),那么你将会失去 \(a_i\) 分。
令 \(dp_i\) 表示最后一个 \(1\) 在 \(i\) 时的最大分数。
如果说某个从 \(i\rightarrow j\) 的转移满足 \(i<l_k 且 j>r_k\),那么你将会失去 \(a_k\) 分,因为在 \([i+1,j-1]\) 中全都是 \(0\)。
可以考虑用线段树维护。我们对每个 \(i\) 在 \(r_i+1\) 处记录区间 \(i\)。那么当你在求 \(dp_j\) 时,就可以将所有该位置记录的 \([1,l_i-1]\) 的 \(dp\) 值减 \(a_i\)。因为这种转移符合上面的条件,应该减去。
空间复杂度 \(O(N+M)\),时间复杂度 \(O((N+M)\log N)\)。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int MAXN = 200005;
struct Segment_Tree {
int l[MAXN << 2], r[MAXN << 2];
ll Max[MAXN << 2], lazy[MAXN << 2];
void build(int u, int s, int t) {
l[u] = s, r[u] = t, Max[u] = lazy[u] = 0;
if(s == t) {
return;
}
int mid = (s + t) >> 1;
build(u << 1, s, mid), build((u << 1) | 1, mid + 1, t);
}
void tag(int u, ll x) {
Max[u] += x, lazy[u] += x;
}
void pushdown(int u) {
tag(u << 1, lazy[u]), tag((u << 1) | 1, lazy[u]), lazy[u] = 0;
}
void update(int u, int s, int t, ll x) {
if(l[u] >= s && r[u] <= t) {
tag(u, x);
return;
}
pushdown(u);
if(s <= r[u << 1]) {
update(u << 1, s, t, x);
}
if(t >= l[(u << 1) | 1]) {
update((u << 1) | 1, s, t, x);
}
Max[u] = max(Max[u << 1], Max[(u << 1) | 1]);
}
ll Getmax(int u, int s, int t) {
if(s > t) {
return 0;
}
if(l[u] >= s && r[u] <= t) {
return Max[u];
}
ll x = -(ll)(1e18);
if(s <= r[u << 1]) {
x = max(x, Getmax(u << 1, s, t));
}
if(t >= l[(u << 1) | 1]) {
x = max(x, Getmax((u << 1) | 1, s, t));
}
return x;
}
}tr;
int n, m;
ll dp[MAXN], sum, Max;
vector<pii> ve[MAXN];
signed main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m;
for(int i = 1, l, r, x; i <= m; ++i) {
cin >> l >> r >> x;
ve[r + 1].push_back({l, -x});
sum += x;
}
tr.build(1, 1, n + 1);
for(int i = 1; i <= n + 1; ++i) {
for(auto [l, x] : ve[i]) {
tr.update(1, 1, l, x);
}
dp[i] = tr.Getmax(1, 1, i);
if(i <= n) {
tr.update(1, i + 1, i + 1, dp[i]);
}
Max = max(Max, dp[i]);
}
cout << sum + Max;
return 0;
}