CF500F New Year Shopping Sol

线段树分治板题。

对于这道题目,看到时间点,想到对时间轴建树,对于每一次插入和删除操作,都在线段树区间上进行区间修改(标记)来完成。

具体保证时间复杂度的方法其实和线段树是一样的。

就是一段区间可以分为 \(\log_2\) 级别的多段区间。
类似于线段树的区间分解,分别对区间进行 tag 标记。
显然复杂度为 \(\log\),保证了全局 \(O(4000N \log T)\) 的优秀复杂度。

对于 DP,可以维护一个 DP 数组,分为 \(\log\) 层。
对于每一层下放,可以直接复制上一层的 DP 数组。
随后加入当前区间标记过的所有物品,进行 DP 即可。

具体可以看代码。

#include <bits/stdc++.h>
#define ls ((rt) << 1)
#define rs ((rt) << 1 | 1)
#define pb push_back
#define mp make_pair
#define int long long
using namespace std;

template <typename T> inline void read(T &x) {
  x = 0; char ch = getchar();
  while (!isdigit(ch)) ch = getchar();
  while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
}

const int N = 2e4 + 10;
int n, p, Q, res[N], dp[21][N];
vector <pair <int, int> > qry[N], t[N << 2];

inline void upd(int rt, int l, int r, int L, int R, int c, int h) {
  if (L <= l && r <= R) return t[rt].pb(mp(c, h)), void();
  int mid = (l + r) >> 1;
  if (L <= mid) upd(ls, l, mid, L, R, c, h);
  if (R > mid) upd(rs, mid + 1, r, L, R, c, h); return ;
}

inline void dfs(int rt, int l, int r, int dep) {
  for (int i = 0; i <= 4000; ++i) dp[dep][i] = dp[dep - 1][i];
  for (auto souv: t[rt]) {
    for (int i = 4000; i >= souv.first; --i)
      dp[dep][i] = max(dp[dep][i], dp[dep][i - souv.first] + souv.second);
  }
  if (l == r) {
    for (auto q: qry[l]) res[q.first] = dp[dep][q.second];
    return ;
  }
  int mid = (l + r) >> 1;
  dfs(ls, l, mid, dep + 1), dfs(rs, mid + 1, r, dep + 1);
  return ;
}

signed main() {
  ios_base::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  cin >> n >> p;
  for (int i = 1; i <= n; ++i) {
    int c, h, t; cin >> c >> h >> t;
    upd(1, 1, 20000, t, t + p - 1, c, h);
  }
  cin >> Q;
  for (int i = 1; i <= Q; ++i) {
    int a, b; cin >> a >> b;
    qry[a].pb(mp(i, b));
  }
  dfs(1, 1, 20000, 1);
  for (int i = 1; i <= Q; ++i) cout << res[i] << endl;
  return 0;
}
posted @ 2022-07-18 22:05  MistZero  阅读(33)  评论(0编辑  收藏  举报