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
浙公网安备 33010602011771号