Codeforces 702F. T-Shirts

Codeforces 702F. T-Shirts

很容易想到一个暴力做法: 贪心地将T恤按 \(q_i\) 从大到小, 若 \(q_i\) 相等则 \(c_i\) 从小到大排序后, 每一次从 \(1\sim n\) 扫一遍, 能买就买, 最后得出件数. 暴力代码如下:

sort(A + 1, A + n + 1, [&](const T_shirt &x, const T_shirt &y)->bool {
	return x.q > y.q || (x.q == y.q && x.c < y.c);
});
for (int i = 1; i <= m; ++i) {
  int v = V[i];
  int cnt = 0;
  for (int j = 1; j <= n; ++j) {
    if (v >= A[j].c) {
      v -= A[j].c;
      ++cnt;
    }
  }
  Ans[i] = cnt;
}

这样时间复杂度为 \(\mathcal O(nm)\) 无法通过. 于是考虑优化.

但是我们会发现, 这样子的暴力不太好优化. 由于 \(v\) 可能会变, 我们并不知道在哪些位置会有贡献.

  • \(\textbf{trick}\) 思考用数据结构优化暴力时, 可以尝试交换循环顺序.

于是我们考虑换一种暴力的方式. 我们可以考虑交换二重循环的顺序, 把T恤排序之后, 先枚举T恤的顺序, 然后对于每一个T恤枚举每一个人是否能买这个T恤. 暴力代码如下:

sort(A + 1, A + n + 1, [&](const T_shirt &x, const T_shirt &y)->bool {
	return x.q > y.q || (x.q == y.q && x.c < y.c);
});
for (int i = 1; i <= m; ++i) Ans[i] = 0;
for (int j = 1; j <= n; ++j) {
  for (int i = 1; i <= m; ++i) {
    if (V[i] >= A[j].c) {
      V[i] -= A[j].c;
      ++Ans[i];
    }
  }
}

这样时间复杂度还是 \(\mathcal O(nm)\) 无法通过.

但这样子的暴力是可以使用数据结构优化的: 用数据结构维护每一个人剩下的钱. 那么对于一个T恤, 其在数据结构上的操作为: \(\forall x\ge c,\,x\leftarrow x-c\) . 显然可以平衡树打懒标记维护.

但出现了一个问题: 平衡树执行完该操作之后, 原序列的顺序会改变, 即不能保证平衡二叉搜索树的性质.

于是我们可以考虑再设一个阈值为 \(2c\) . 对于 \(\forall x\ge 2c\) 可以平衡树上打懒标记维护, 因为此时 \(x-c\ge c\) , 即不会影响平衡树的顺序. 而 \(\forall x\in [c,\,2c)\) 直接暴力插入. 由于当 \(x\in [c,\,2c)\) 时, \(x-c<c\) , 即 \(c>\dfrac{x}2\) . 每一次减去的值至少是原数的一半, 因此对于每一个数最多执行 \(\log\) 次这样的操作. 总时间复杂度即为 \(\mathcal O(n\log n\log V)\) .

参考代码
#include <bits/stdc++.h>
using namespace std;
std::uint64_t randu64(void) {
  static std::uint64_t seed = std::chrono::steady_clock::now().time_since_epoch().count();
	std::uint64_t z = (seed += (0x9E3779B97F4A7C15ull));
	z = (z ^ (z >> 30)) * (0xBF58476D1CE4E5B9ull);
	z = (z ^ (z >> 27)) * (0x94D049BB133111EBull);
	return z ^ (z >> 31);
} // randu64
static constexpr int Maxn = 2e5 + 5;
int n, m;
int ans[Maxn];
struct shirt {
  int64_t c, q;
  shirt() = default;
  friend bool operator < (const shirt &lhs, const shirt &rhs) {
    return lhs.q > rhs.q || (lhs.q == rhs.q && lhs.c < rhs.c);
  }
} a[Maxn];
struct node {
  int ls, rs, size;
  uint64_t fix;
  int64_t val, lz;
  int id;
  int ans, alz;
  node() = default;
  node(int64_t c, int id) : 
    ls(0), rs(0), fix(randu64()), size(1), 
    val(c), lz(0), id(id), ans(0), alz(0) { }
} tr[Maxn];
int root, tot;
template<typename ...Args>
inline int newnode(Args ...args) { tr[++tot] = node(args...); return tot; }
__attribute__((__always_inline__)) inline
void pushup(int p) {
  if (!p) return ;
  tr[p].size = tr[tr[p].ls].size + tr[tr[p].rs].size + 1;
} // pushup
void pushtag(int p, int64_t v) {
  if (!p) return ;
  tr[p].lz += v; tr[p].val += v;
} // pushtag
void pushatag(int p, int c) {
  if (!p) return ;
  tr[p].ans += c, tr[p].alz += c;
} // pushatag
void pushdown(int p) {
  if (!p) return ;
  if (tr[p].lz != 0) {
    pushtag(tr[p].ls, tr[p].lz);
    pushtag(tr[p].rs, tr[p].lz);
    tr[p].lz = 0;
  }
  if (tr[p].alz != 0) {
    pushatag(tr[p].ls, tr[p].alz);
    pushatag(tr[p].rs, tr[p].alz);
    tr[p].alz = 0;
  }
} // pushdown
int join(int l, int r) {
  if (!l || !r) return l | r;
  pushdown(l);
  pushdown(r);
  if (tr[l].fix < tr[r].fix) {
    tr[l].rs = join(tr[l].rs, r);
    pushup(l);
    return l;
  } else {
    tr[r].ls = join(l, tr[r].ls);
    pushup(r);
    return r;
  }
} // join
void split(int p, int64_t k, int &l, int &r) {
  if (!p) l = r = 0;
  else {
    pushdown(p);
    if (tr[p].val <= k) {
      l = p;
      split(tr[p].rs, k, tr[l].rs, r);
      pushup(l);
    } else {
      r = p;
      split(tr[p].ls, k, l, tr[r].ls);
      pushup(r);
    }
  }
} // split
int sz, v[Maxn];
void traverse(int p) {
  if (!p) return ;
  pushdown(p);
  traverse(tr[p].ls);
  v[++sz] = p;
  traverse(tr[p].rs);
} // traverse
int main(void) {
  scanf("%d", &n);
  for (int i = 1; i <= n; ++i)
    scanf("%lld%lld", &a[i].c, &a[i].q);
  sort(a + 1, a + n + 1);
  scanf("%d", &m);
  root = 0;
  for (int i = 1; i <= m; ++i) {
    int64_t x; scanf("%d", &x);
    int A, B; split(root, x, A, B);
    root = join(A, join(newnode(x, i), B));
  }
  for (int i = 1; i <= n; ++i) {
    int A, B, C, D;
    int64_t w = a[i].c;
    split(root, w - 1, A, B);
    split(B, 2 * w - 1, B, C);
    pushtag(C, -w);
    pushatag(C, 1);
    pushatag(B, 1);
    root = join(A, C);
    sz = 0; traverse(B);
    for (int j = 1; j <= sz; ++j) {
      tr[v[j]].val -= w;
      tr[v[j]].ls = 0, tr[v[j]].rs = 0;
      split(root, tr[v[j]].val, B, D);
      root = join(B, join(v[j], D));
    }
  }
  sz = 0; traverse(root);
  assert(sz == m);
  for (int i = 1; i <= sz; ++i)
    ans[tr[v[i]].id] = tr[v[i]].ans;
  for (int i = 1; i <= m; ++i)
    printf("%d%c", ans[i], " \n"[i == m]);
  exit(EXIT_SUCCESS);
} // main
posted @ 2021-11-30 10:21  cutx64  阅读(85)  评论(0)    收藏  举报