五月份赛时写了五个题,这次(赛后三个月)个人 VP 了剩下的题目,耗时接近六个小时又写了五个题,括号里是 VP 的罚时,从 300 开始计,没有标记罚时的是五月份赛时写的。
一个人打很容易在一条路上走死,遇到 WA 也找不出 bug……六个小时有四个小时在想为什么 WA,有队友讨论检查的话罚时会少很多……
个人评价难度:
- 签到:I
- 比较基础,广义签到:AKJ
- 略有思维难度:CF
- 需要细心思考:DHM
- 很天才的想法:EL
- 暂时做不了的:BG
A. Printer
题意 有 n n n 台打印机,第 i i i 台打印机每 t i t_{i} ti 秒打印一份试题,但每次打印 l i l_{i} li 份试题后要休息 w i w_{i} wi 秒。如果所有打印机同时工作,打印 k k k 份题至少要多久。
数据范围: n ⩽ 100 n \leqslant 100 n⩽100,其余数据 ⩽ 1 × 1 0 9 \leqslant 1\times10^{9} ⩽1×109。
思路 题目不难,二分答案即可。唯一考点在于二分要提前返回,否则会爆 LL。
时间复杂度 Θ ( n log v ) \Theta(n\log v) Θ(nlogv),其中 v = 2 × 1 0 18 v=2\times 10^{18} v=2×1018。
void eachT() {
int n = read(), k = read();
vector<ll> t(n), l(n), w(n);
for (int i = 0; i < n; ++i) {
t[i] = read(), l[i] = read(), w[i] = read();
}
auto check = [&](ll mid) {
ll sum = 0;
for (int i = 0; i < n; ++i) {
ll q = mid / (t[i] * l[i] + w[i]), r = mid % (t[i] * l[i] + w[i]);
sum += q * l[i] + min(r / t[i], l[i]);
if (sum >= k) return true;
}
return false;
// return sum >= k; <- sum爆LL
};
ll lt = 1, rt = 2e18;
while (lt <= rt) {
ll mid = (lt + rt) >> 1;
if (check(mid)) rt = mid - 1;
else lt = mid + 1;
}
printf("%lld\n", lt);
}
C. Colorful Segments 2 (447 + 2)
题意 给定数轴上的 n n n 条线段,每条线段可以涂上 k k k 种颜色的一种,要求颜色相同的线段不能相交。求方案数。
数据范围: n ⩽ 5 × 1 0 5 n \leqslant 5\times 10^{5} n⩽5×105,其余数据 ⩽ 1 × 1 0 9 \leqslant 1\times10^{9} ⩽1×109。
思路 按左端点排个序,然后依次染色,如果当前线段与已经染色的 t t t 条线段相交,对答案的贡献是 k − t k-t k−t。将所有贡献乘起来。
如果用线段树,则是区间加、区间最大值,也可以用堆 (multiset) 维护。
时间复杂度 Θ ( n log v ) \Theta(n\log v) Θ(nlogv),其中 v = 1 × 1 0 9 v=1\times 10^{9} v=1×109。
constexpr int mod = 998244353;
constexpr int N = 5e5 + 8;
struct SMT {
struct info {
int mx;
int mrk;
int l, r;
} t[N << 6];
void push_up(info& p, info l, info r) {
p.mx = max(l.mx, r.mx);
}
void push_down(info& p, int d, int l) {
p.mx += d;
p.mrk += d;
}
// ...
} smt;
void eachT() {
int n = read(), k = read();
vector<pair<int, int>> a(n);
for (auto& [l, r] : a) {
l = read(), r = read();
}
sort(a.begin(), a.end());
smt.init(1, 1e9);
ll res = 1;
for (auto& [l, r] : a) {
auto qq = smt.query(l, r);
res *= k - qq.mx;
res %= mod;
smt.modify(l, r, 1);
}
printf("%lld\n", res);
}
D. Hero of the Kingdom (514 + 3)
队友比较擅长这种题,我自己做写得又慢错误率又高……
题意 买入 x x x 件商品需要 a x + b ax + b ax+b 秒和 p x px px 金币,卖出 x x x 件商品需要 c x + d cx + d cx+d 秒赚 q x qx qx 金币。有 t t t 秒以及初始 m m m 金币,问 t t t 秒结束后最多能有多少金币。
思路 首先有两个结论:尽可能多地买,最终全卖(否则可以不买)。于是问题化为花费 ( a + c ) x + ( b + d ) (a+c)x+(b+d) (a+c)x+(b+d) 秒赚 ( q − p ) x (q-p)x (q−p)x 金币,其中 x ⩽ ⌊ m q ⌋ x \leqslant \lfloor \cfrac{m}{q} \rfloor x⩽⌊qm⌋。
思考能不能暴力模拟,不能。最坏情况 q − p = 1 q-p=1 q−p=1,且 x x x 始终为 1 1 1,需要模拟大约 t t t 次,时间不允许。
进一步思考如何用计算代替模拟。如果 x x x 始终不变,我们可以把这一部分的模拟合并为一次,具体来说:
一次性最多买 x x x 个,且 p x ⩽ m < p ( x + 1 ) px \leqslant m<p(x+1) px⩽m<p(x+1),要求 x x x 始终不变,设这样最多买 l l l 轮次后 x x x 才会变化,有 m + ( q − p ) x ⋅ l ⩾ p ( x + 1 ) m+(q-p)x\cdot l \geqslant p(x+1) m+(q−p)x⋅l⩾p(x+1),化简得 l = ⌈ p ( x + 1 ) − m ( q − p ) x ⌉ l=\left\lceil \cfrac{p(x+1)-m}{(q-p)x} \right\rceil l=⌈(q−p)xp(x+1)−m⌉。
那买 l l l 轮的时间够吗?不一定,需要满足 l ⋅ [ ( a + c ) x + ( b + d ) ] ⩽ t l\cdot[(a+c)x+(b+d)] \leqslant t l⋅[(a+c)x+(b+d)]⩽t,因此 l l l 对