CodeCraft-21 and Codeforces Round 711 (Div. 2)

CodeCraft-21 and Codeforces Round 711 (Div. 2)

A. GCD Sum

题意

定义 \(gcdSum(x)=gcd(x,sum\ of\ digits\ of\ x)\)

请你输出最小的 \(x\) 满足 \(x\ge n\),且 \(gcdSum(x)>1\)

做法

直接暴力遍历即可,因为由 \(3\) 的倍数的性质可知,当

数位和是 \(3\) 的倍数时,原数也是 \(3\) 的倍数。故最多 \(3\) 次之后 \(gcdSum(x)\) 必然大于 \(1\)

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
 
 
void solve() {
	LL n;
	cin >> n;
	for (LL i = n; ; i++) {
		LL sum = 0;
		LL x = i;
		while (x) {
			sum += x % 10;
			x /= 10;
		}
		if (__gcd(sum, i) > 1) {
			cout << i << '\n';
			break;
		}
	}
}
 
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int t;
	cin >> t;
	while (t--) {
		solve();
	}
	
	return 0;
}

B. Box Fitting

题意

给你 \(n\) 个高为 \(1\),宽为 \(2\) 的次幂的矩形,以及一个宽为 \(W\) 的盒子,你现在要把这些矩形塞进去,可以叠加,但宽度不能超过 \(W\),请问盒子高度最少是多少?

做法

考虑一个贪心,每层都尽可能先放大的,如果放不下就把高度 \(+1\)

因为我们可以假设最优解,我们把最优解每层的块降序排列,并不改变结果,然后通过交换法可以证明我们贪心解是正确的。且这样做,我们可以保证上层的宽度一定不会大于下层的宽度。(由于 \(2\) 的次幂的原因)。

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
 
 
void solve() {
	int n, W;
	cin >> n >> W;
	vector<int> cnt(35, 0);
	for (int i = 1; i <= n; i++) {
		int x;
		cin >> x;
		cnt[__lg(x)]++;
	}
	int ans = 0;
	while (true) {
		int len = W;
		for (int i = 20; i >= 0; i--) {
			while (cnt[i] && len >= (1 << i)) {
				cnt[i]--;
				len -= 1 << i;
			}
		}
		if (len == W) break;
		ans++;
	}
	cout << ans << '\n';
}
 
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int t;
	cin >> t;
	while (t--) {
		solve();
	}
	
	return 0;
}

C. Planar Reflections

题意

你有一个 \(k\) 级的衰变粒子,前面有 \(n\) 面墙,当级别为 \(x\) 的粒子穿过一面墙的时候,若 \(x>1\) ,它会反射出一个

\(x-1\) 级的粒子,往回走,同时自己继续穿越后续的墙;若 \(x=1\) 则不发生反射。

请问一个 \(k\) 级的衰变粒子,穿越 \(n\) 面墙,一共能产生多少粒子?

做法

观察到数据范围 \(1\leq n,k\leq 1000\),可以考虑 DP。

\(dp[i][j]\) 表示级别为 \(i\) 的粒子,前面还需要穿过 \(j\) 面墙产生的粒子总数。

不难得到如下转移方程:

\[dp[i][j] = dp[i][j-1]+dp[i-1][n-j] \]

还需穿过 \(j\) 面墙,说明此时,我们在第 \(n-j+1\) 号墙面前,准备穿过它,那么我们会反射一个 \(i-1\) 级的粒子

\(n-j\) 开始穿越到 \(1\) 号,一共 \(n-j\) 面墙,然后 \(i\) 级粒子继续从 \(n-j+2\) 号到 \(n\) 号墙,一共 \(j-1\) 面墙。

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
 
void solve() {
	int n, k;
	cin >> n >> k;
	vector<vector<int>> dp(k + 1, vector<int>(n + 1, 0));
	const int mod = 1e9 + 7;
	//表示衰变为 i 的粒子,前面还有 j 面墙要穿越产生的粒子数
	for (int i = 1; i <= k; i++) dp[i][0] = 1;
	
	for (int i = 1; i <= k; i++) {
		for (int j = 1; j <= n; j++) {
			dp[i][j] = (dp[i][j - 1] + dp[i - 1][n - j]) % mod;
		}
	} 
	cout << dp[k][n] << "\n";
}
 
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
 
	int t;
	cin >> t;
	while (t--) {
		solve();
	}
 
 
	return 0;
}

D. Bananas in a Microwave

题意

你有 \(n\) 个类型的操作,以及一个初始值 \(k=0\)

在第 \(i\) 次操作,你必须按如下规则执行。

  • \(t_i=1\),你可以选择执行 \(a_i\)\(k\rightarrow \lceil k+x\rceil\) ,其中 \(0\leq a_i\leq y\)
  • \(t_i=2\),你可以选择执行 \(a_i\)\(k\rightarrow \lceil k\times x\rceil\) ,其中 \(0\leq a_i\leq y\)

现在问你对于 \(1\leq i\leq m\),把 \(k\) 变成 \(i\) 最少要几次操作。

做法

看数据范围 \(1\leq n\leq 100,1\leq m\leq 10^5\)

我们不难想到一个暴力的 DP,我们设 \(dp[i][j]\) 表示考虑了前 \(i\) 次操作,值为 \(j\) 能不能被变出来。

转移就暴力枚举 \(0\leq a_i\leq y\) ,执行了几次,这样复杂度是 \(O(nm^2)\),显然要超时了。

考虑优化,这是我们就要思考,我们有没有必要每次都 for 满这个 \(y\)

假设目前有个操作是 \(x=2\)\(y=6\),我们枚举的 \(j=3\),那么我们会依次更新:

\(3,5,7,9,11,13,15\),我们思考如果我们会枚举到 \(j=7\),我们是不是会依次更新:\(7,9,11,13,15,17,19\)

我们会发现此时 \(j=7\) 的更新范围是覆盖了从 \(j=3\) 往后更新的范围的,因为我们每次都是 \(+x\)

那么就暗示我们 \(dp[i+1][7\backsim15]\) 这个状态我们不需要再以 \(j=3\) 为起点,暴力 for 次数去更新了,

因为我们知道当我们枚举到 \(j=7\) 为起点开始暴力 for 操作次数的时候,必然会更新 \(dp[i+1][7\backsim 15]\) 这些状态。所以我们就得到了优化,当 \(dp[i][cur]\)\(true\) 是显然不需要再往下暴力更新了。

这样每一个 \(j\) 只会遍历一次,所以复杂度就是 \(O(nm)\) 的。乘法的操作也是同理。

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
 
void solve() {
	int n, m;
	cin >> n >> m;
	vector<int> dp(m + 1, 0), ans(m + 1, n + 1);
	dp[0] = 1;
	for (int i = 1; i <= n; i++) {
		int t, y;
		LL x;
		cin >> t >> x >> y;
		vector<int> ndp = dp;
		for (int j = 0; j <= m; j++) {
			LL cur = j;
			if (!dp[cur]) continue;
			for (int k = 1; k <= y; k++) {
				if (t == 1) cur = (cur * 100000 + 100000 + x - 1) / 100000;
				else cur = (cur * x + 100000 - 1) / 100000; 
				if (cur > m) break;
				if (dp[cur]) break;
				ndp[cur] = 1;
			}
		}
		dp.swap(ndp);
		for (int j = 1; j <= m; j++) {
			if (dp[j]) ans[j] = min(ans[j], i);
		}
	}
	
	for (int i = 1; i <= m; i++) {
		if (ans[i] == n + 1) ans[i] = -1;
		cout << ans[i] << " \n"[i == m];
	}
}
 
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
 
	int t;
	t = 1;
	while (t--) {
		solve();
	}
 
 
	return 0;
}
posted @ 2023-11-09 23:20  jackle  阅读(6)  评论(0编辑  收藏  举报