2020CCPC绵阳站

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值