Codeforces Round #834 (Div. 3)(D-G)

D. Make It Round

可知题意是让求一定操作数内要能得到的数末尾的0最多是几

根据数的唯一分解定理,末尾为0的个数只取决于2和5的数量,因为最多18个0,也就是最多18对2和5,然后得到对应的操作次数即可

AC代码:

/*##########################################################
     # File Name: 2.cpp
     # Author: HideInTheSea
     # Created Time: 2022年11月20日 星期日 19时24分47秒
##########################################################*/
#include 
using namespace std;
using LL = long long;
void Solve() {
	LL n, m;
	cin >> n >> m;
	for (int i = 18; i >= 0; i--) {
		LL x = n;
		int cnt2 = i, cnt5 = i;
		while (x % 2 == 0) {
			x /= 2;
			cnt2--;
		}
		while (x % 5 == 0) {
			x /= 5;
			cnt5--;
		}
		LL y = 1;
		for (int j = 0; j < cnt2; j++) {
			y *= 2;
		}
		for (int j = 0; j < cnt5; j++) {
			y *= 5;
		}
		if (y <= m) {
			LL k = m / y * y;
			cout << n * k << '\n';
			return;
		}
	}
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
	int T;
	cin >> T;
	while (T--) {
		Solve();
	}
    return 0;
}

E. The Humanoid

挺不错的DP题

主角有两瓶绿色药水和一瓶蓝色药水

主角每秒可以执行三种操作:

1.主角吸收随从能量的一半

2.主角喝绿色药水能量乘2

3.主角喝蓝色药水能量乘3

问最多吸收多少随从

首先贪心的去想随从能量越小,越要放在前面,这样最容易吸收他的能量而不浪费药水,然后再考虑DP

状态边界为dp[0][i][j],分别为喝了i瓶绿色药水和喝了j瓶蓝色药水

dp[i][j][k]表示到达第i秒用了j瓶绿色药水和k瓶蓝色药水时最大的能量值和吸收随从的次数,转移就是分别模拟三种操作

AC代码:

/*##########################################################
     # File Name: 1.cpp
     # Author: HideInTheSea
     # Created Time: 2022年11月22日 星期二 18时06分06秒
##########################################################*/
#include 
using namespace std;
using LL = long long;
void Solve() {
	int n;
	LL h;
	cin >> n >> h;
	vector a(n + 1);
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	sort(a.begin() + 1, a.end());
	vector>>> dp(n + 1, vector>> (3, vector> (2)));
	for (int i = 0; i < 3; i++) {
		for (int j = 0; j < 2; j++) {
			dp[0][i][j].first = h * (i == 0 ? 1 : 2) * (j == 0 ? 1 : 3) * (i == 2 ? 2 : 1);
			dp[0][i][j].second = 0;
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j < 3; j++) {
			for (int k = 0; k < 2; k++) {
				dp[i][j][k] = make_pair(dp[i - 1][j][k].first + (dp[i - 1][j][k].first > a[i]) * a[i] / 2, dp[i - 1][j][k].second + (dp[i - 1][j][k].first > a[i]));
			}
		}
		dp[i][0][1] = max(dp[i][0][1], pair {dp[i][0][0].first * 3, dp[i][0][0].second});
		dp[i][1][1] = max({dp[i][1][1], pair {dp[i][1][0].first * 3, dp[i][1][0].second}, pair {dp[i][0][1].first * 2, dp[i][0][1].second}});
		dp[i][1][0] = max(dp[i][1][0], pair {dp[i][0][0].first * 2, dp[i][0][0].second});
		dp[i][2][1] = max({dp[i][2][1], pair {dp[i][2][0].first * 3, dp[i][2][0].second}, pair {dp[i][1][1].first * 2, dp[i][1][1].second}});
		dp[i][2][0] = max(dp[i][2][0], pair {dp[i][1][0].first * 2, dp[i][1][0].second});
	}
	cout << dp[n][2][1].second << '\n';
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
	int T;
	cin >> T;
	while (T--) {
		Solve();
	}
    return 0;
}

F. All Possible Digits

首先要看清题干,给出一段在p进制下的数,虽然每次操作可以选择任意一位数,但最后写在黑板上的是x+1,也就是说每次操作只能最低位+1

那么首先想到的是进行p-1次操作一定能完成任务,但操作数太多了,题目要最小化

其次想到最低位是否要进位?

如果不进位,前提是除最低位以外的其他位要全部包含0到(最低位-1)的所有数,否则必须通过进位来取到不包含的数,然后在考虑除最低位以外的数在(最低位+1)到(p-1)中最大的不存在的数,这是计算的边界,因为n<=100,所以不需要考虑时间问题,暴力即可

如果进位,那么先把要进位的数按运算法则运算,得到最终的进位后的数,如果都是(p-1),进位后会在最高位前面多出一个1,运算的时候也要存下出现过的数,最后就是最低位置为0,然后暴力查找最大的没表示到的数,暴力查找时,因为大于等于最低位的数都在进位时遍历过了,所以直接从(最低位-1)开始暴力的遍历即可

AC代码:

/*##########################################################
     # File Name: 1.cpp
     # Author: HideInTheSea
     # Created Time: 2022年11月23日 星期三 18时43分30秒
##########################################################*/
#include 
using namespace std;
using LL = long long;
void Solve() {
	int n, p;
	cin >> n >> p;
	vector a(n + 1);
	map mp;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		mp[a[i]] = 1;
	}
	if (mp.size() == p) {
		cout << "0\n";
		return;
	}
	function check = [&](int x, int y) {
		for (int i = x; i <= y; i++) {
			if (mp.count(i) == 0) {
				return false;
			}
		}
		return true;
	};
	function check1 = [&](int x, int y) {
		for (int i = y; i > x; i--) {
			if (mp.count(i) == 0) {
				return i;
			}
		}
		return x;
	};
	int maxx = check1(a[n] + 1, p - 1);
	if (check(0, a[n])) {
		if (maxx == a[n]) {
			cout << p - a[n] - 1 << '\n';
		} else {
			cout << maxx - a[n] << '\n';
		}
	} else {
		LL ans = p - a[n];
		int x = a[n];
		a[n] = 0;
		mp[a[n]] = 1;
		for (int i = n - 1; i >= 0; i--) {
			a[i]++;
			if (a[i] == p) {
				a[i] = 0;
			} else {
				mp[a[i]] = 1;
				break;
			}
		}
		function calc = [&](int x, int y) {
			for (int i = y - 1; i >= x; i--) {
				if (mp.count(i) == 0) {
					return i;
				}
			}
			return 0;
		};
		cout << ans + calc(0, x) << '\n';
	}
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
	int T;
	cin >> T;
	while (T--) {
		Solve();
	}
    return 0;
}

G. Restore the Permutation

看到题,首先考虑贪心的把越小的数越要放到前面,然后根据题目的描述得知,给出的数组中的数是相邻两个数之间最大的数,因此要把这个数放到第二个位置,这样才能保证字典序最小

根据样例能够看出,如果一个数出现了两次,或者出现的无论如何都没法组成全排列的话就输出-1

再进行构造答案,首先根据字典序,标号小的一定要往前放,再考虑b中的数是相邻两个数之间最大的数,那么要尽可能地让他俩之差小,这样标号小地数才能够尽可能的往前放,在遍历的时候应该从标号大的数递减遍历,这样能够让没在b中出现的相对比较大的数放在靠后的位置,如果一个数在b中出现过,就把该数下标放到堆里,这样既能保证b中的数是相邻两个数之间最大的,也能保证两个数之差最小,这样才能使得最后的全排列字典序最小

AC代码:

/*##########################################################
     # File Name: 2.cpp
     # Author: HideInTheSea
     # Created Time: 2022年11月22日 星期二 20时43分24秒
##########################################################*/
#include 
using namespace std;
using LL = long long;
void Solve() {
	int n;
	cin >> n;
	vector a(n + 1), b(n + 1);
	bool ok = true;
	for (int i = 2; i <= n; i += 2) {
		cin >> a[i];
		b[a[i]] = i;
	}
	priority_queue q;
	for (int i = n; i >= 1; i--) {
		if (!b[i]) {
			if (q.empty()) {
				ok = false;
				break;
			} else {
				a[q.top() - 1] = i;
				q.pop();
			}
		} else {
			q.push(b[i]);
		}
	}
	if (!ok) {
		cout << "-1\n";
	} else {
		for (int i = 1; i <= n; i++) {
			cout << a[i] << " \n"[i == n];
		}
	}
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
	int T;
	cin >> T;
	while (T--) {
		Solve();
	}
    return 0;
}

你可能感兴趣的:(AK-CF-ABC,算法,c++,开发语言)