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;
}
posted @ 2024-09-16 17:01  Yaosicheng124  阅读(4)  评论(0编辑  收藏  举报