D - Defuse the Bombs
题意:
有n个炸弹,每个炸弹都有自己的时间,有三个步骤:
1. 选择一个炸弹时间加一。
2. 让所有炸弹的时间减一。
3. 当其中有一个炸弹的时间变为负数,那么所有炸弹全部爆炸,否则返回步骤一。
我们需要求出最多执行步骤一的次数。
思路:
考虑贪心操作。即每次都选取最小爆炸时间的炸弹,对其爆炸时间进行延长。可以发现对于每个操作次数num,对于初始爆炸时间序列a[i]统计负差值和。这个负差值和即为对于当前操作次数,如果保证所有炸弹不爆炸所需要的最小操作次数。那么根据这个思路,我们可以直接二分答案,每次check一下mid即可。
代码:
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll t, n;
ll a[100010];
int main() {
scanf("%lld", &t);
for (int i = 1; i <= t; i++) {
scanf("%lld", &n);
for (int j = 1; j <= n; j++) {
scanf("%lld", &a[j]);
}
ll l = 1, r = 1e10, ans = 1;
while (l <= r) {
ll sum = 0;
ll mid = (l + r) / 2;
for (int k = 1; k <= n; k++) {
if (a[k] < mid) {
sum += mid - a[k];
}
//cout << l << ' '<< r << ' ' << sum << endl;
}
if (sum <= mid) {
ans = mid + 1;
l = mid + 1;
}
else r = mid - 1;
//cout << ans <<'*' << endl;
}
printf("Case #%d: %lld\n", i, ans);
}
return 0;
}
J - Joy of Handcraft
题意:
给定n个灯泡,那个灯泡排列在电路板上,然后每个灯泡给定你一个t和x,这个灯泡会在[2kt + 1,2kt + t],k=(0,1,2…)的时间区间内发出亮度为x的光,其他时间内不发光,然后给定你一个m问你:从第一秒到第m秒每一秒时亮度最大的灯的亮度为多少?
思路:
这道题对于给定的m个区间,因为求得是最大的的亮度为多少,所以我们先排个序,将重复时间区间的最大的亮度拿出来。这样再去枚举每个t对应的时间是,最坏的情况就是我们将时间去完重之后,从1到m这个m个时间点都有灯泡发光。时间为1那么就会有m/1个区间,这样依次排下去,区间总个数就是(m/1 + m/2 + m/3+…+m/m)调和级数可知一共就是mlogm个区间(这就可做了嘛),枚举更新每个区间时间复杂度也就是O(m * logm * logn)的时间复杂度,2s 跑 400ms就过去了。
剩下的就是区间更新的基本操作了。
代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
int n, m, t;
struct Node {
int t, x;
}a[N], b[N];
struct node {
int l, r;
int w;
int lazy;
}tr[4 * N];
bool cmp(Node a, Node b) {
if (a.t == b.t) {
return a.x > b.x;
}
else return a.t < b.t;
}
void pushup(int rt) {
tr[rt].w = max(tr[rt << 1].w, tr[rt << 1 | 1].w);
}
void pushdown(int rt) {
auto& root = tr[rt], & left = tr[rt << 1], & right = tr[rt << 1 | 1];
if (root.lazy) {
left.lazy = max(left.lazy, root.lazy);
right.lazy = max(right.lazy, root.lazy);
left.w = max(left.lazy, left.w);
right.w = max(right.lazy, right.w);
root.lazy = 0;
}
}
void build(int rt, int l, int r) {
tr[rt] = { l,r,0,0 };
if (l == r) {
return;
}
int m = l + r >> 1;
build(rt << 1, l, m);
build(rt << 1 | 1, m + 1, r);
pushup(rt);
}
void update(int rt, int L, int R,int c) {
if (L <= tr[rt].l && tr[rt].r <= R) {
tr[rt].lazy = max(tr[rt].lazy, c);
tr[rt].w = max(tr[rt].w, tr[rt].lazy);
}
else {
pushdown(rt);
int m = tr[rt].l + tr[rt].r >> 1;
if (L <= m)update(rt << 1, L, R, c);
if (R > m)update(rt << 1 | 1, L, R, c);
pushup(rt);
}
}
int query(int rt, int L, int R) {
if (L <= tr[rt].l && tr[rt].r <= R) {
return tr[rt].w;
}
pushdown(rt);
int m = tr[rt].l + tr[rt].r >> 1;
int ans = 0;
if (L <= m) ans = max(ans, query(rt << 1, L, R));
if (R > m)ans = max(ans, query(rt << 1 | 1, L, R));
return ans;
}
int main() {
scanf("%d", &t);
for (int i = 1; i <= t; i++) {
scanf("%d %d", &n, &m);
for (int j = 1; j <= n; j++) {
scanf("%d %d", &a[j].t, &a[j].x);
}
sort(a + 1, a + 1 + n, cmp);
b[1] = a[1];
int cnt = 1;
for (int j = 2; j <= n; j++) {
if (b[cnt].t != a[j].t) {
b[++cnt] = a[j];
}
}
build(1, 1, m);
for (int j = 1; j <= cnt; j++) {
for (int k = 0; ; k++) {
int l = 2 * k * b[j].t + 1;
int r = (2 * k + 1) * b[j].t;
r = min(r, m);
if (l <= m && r <= m)update(1, l, r, b[j].x);
if (l >= m || r == m) break;
}
}
printf("Case #%d:", i);
for (int j = 1; j <= m; j++) {
printf(" %d", query(1, j, j));
}
printf("\n");
}
return 0;
}