第十届中国大学生程序设计竞赛 重庆站(CCPC 2024 Chongqing Site)

B. osu!mania

按照题目的公式进行计算,注意四舍五入的精度问题。

#include <bits/stdc++.h>

using namespace std;


using i32 = int32_t;
using i64 = long long;
using ldb = long double;

using vi = vector<int>;
using pii = pair<int,int>;



void solve(){
	int ppmax;
	cin >> ppmax;

	int a, b, c, d, e, f;
	cin >> a >> b >> c >> d >> e >> f;

	ldb acc = (300.0 * a + 300 * b + 200 * c + 100 * d + 50 * e) / (300.0 *(a + b + c + d + e + f));
	ldb tpp = (320.0 * a + 300 * b + 200 * c + 100 * d + 50 * e) * 5 * ppmax / (320.0 * (a + b + c + d + e + f));
	i64 pp = max(0ll, i64(round(tpp)) - 4ll * ppmax);
	cout <<fixed << setprecision(2) << acc*100.0 << "% " << pp << "\n";
	return;	 
}

i32 main() {
	ios::sync_with_stdio(false), cin.tie(nullptr);
	
	int T;
	cin >> T;
	while(T --)
		solve();

	return 0;
}	

C. 连方

如果第一行第七行都是#,则全部都都是#

如果第一行第七行只有一行都是#,则无解。

否则第二行、第六行对第一行、第七行取反,这样可以把第一行和第七行所有#都联通。

然后再第三行第第五行各找一个之和第二行第六行八联通的点,然后通过第四行联通即可。

#include <bits/stdc++.h>

using namespace std;


using i32 = int32_t;
using i64 = long long;
using ldb = long double;

using vi = vector<int>;
using pii = pair<int,int>;



void solve(){
	int n;
	cin >> n;
	vector<string> a(7);
	cin >> a[0] >> a[6];
	bool f0 = false, f6 = false;
	for(auto c : a[0])
		f0 |= (c == '.');
	for(auto c : a[6])
		f6 |= (c == '.');
	if(f0 != f6) {
		cout << "No\n";
		return;
	}
	cout << "Yes\n";
	if(f0 == false) {
		for(int i = 0; i < 7; i ++) cout << a[0] << "\n";
		return;
	}
	for(int i = 1; i < 6; i ++) a[i] = string(n, '.');
	for(int i = 0; i < n; i ++)	if(a[0][i] == '.') a[1][i] = '#';
	for(int i = 0; i < n; i ++)	if(a[6][i] == '.') a[5][i] = '#';
	int l = -1, r = -1;
	for(int i = 0; i < n and l == -1; i ++) {
		if(a[1][i] == '#') continue;
		if(i - 1 >= 0 and a[1][i - 1] == '#') l = i;
		if(i + 1 < n  and a[1][i + 1] == '#') l = i;
	}

	for(int i = 0; i < n and r == -1; i ++) {
		if(a[5][i] == '#') continue;
		if(i - 1 >= 0 and a[5][i - 1] == '#') r = i;
		if(i + 1 <  n and a[5][i + 1] == '#') r = i;
	}
	a[2][l] = a[4][r] = '#';
	if(l > r) swap(l, r);
	if(l == r or l + 1 == r ){
		a[3][l] = '#';
	} else {
		for(int i = l + 1; i < r ; i ++) a[3][i] = '#';
	}
	for(int i = 0; i < 7; i ++)
		cout << a[i] << "\n";
	return;	 
}

i32 main() {
	ios::sync_with_stdio(false), cin.tie(nullptr);
	
	int T;
	cin >> T;
	while(T --)
		solve();

	return 0;
}	

D. 有限小数

对于一个分数,如果是有限小数。则分母质因数分解后一定只包含\(2,5\)。设\(w\)表示为\(b\)除了\(2,5\)外的质因子乘积。则\(d=2^x5^yw\)。找规律可以发现\(d\)一定满足\(d=2^X5^Yw\)的形势。因此有\(\frac a b + \frac c d = \frac{ad+cb}{bd}=k\)。我们要求的\(k\)一定是有限小数,则一定可以表示为\(k=\frac{z}{2^{x+X}5^{y+Y}},z\in \Z\)。因此我们只要解出丢番图方程\(c\times b - z\times w ^2 = -a\times d\)的整数解,并求出\(c\)的最小正整数解即可。

#include <bits/stdc++.h>

using namespace std;


using i64 = long long;

const i64 MAXN = 1e9;
const i64 inf = LLONG_MAX / 2;

i64 exgcd(i64 a, i64 b, i64 &x, i64 &y) {
	if(b == 0){
		x = 1, y = 0;
		return a;
	}
	i64 d = exgcd(b, a % b, y, x);
	y -= a / b * x;
	return d;
}

i64 calc(i64 a, i64 b, i64 c) {
	i64 x, y;
	i64 d = exgcd(a, b, x, y);
	if(c % d != 0) return inf;
	x *= c / d;
	i64 t = abs(b / d);
	return (x % t + t) % t;
}

void solve() {
	i64 a, b;
	cin >> a >> b;

	i64 w = b;
	while(w % 2 == 0) w /= 2;
	while(w % 5 == 0) w /= 5;
	
	if(w == 1) {
		cout << "0 1\n";
		return;
	}

	i64 resc = inf, resd;
	for(i64 p5 = w; p5 <= MAXN; p5 *= 5)
		for(i64 d = p5, c; d <= MAXN; d *= 2) {
			c = calc(b, w * w, - a * d);
			if(c < resc) resc = c, resd = d;
		}
	cout << resc << " " << resd << "\n";
	return;
}

int main() {
	int T;
	cin >> T;

	while(T --)
		solve();

	return 0;
}

E. 合成大西瓜

对于度为\(1\)的点,只能是\(x,z\)。因此能保存下的只有可能是次大值。否则,则可以是\(x,y,z\),一定可以保存下最大值。

#include <bits/stdc++.h>

using namespace std;


using i32 = int32_t;
using i64 = long long;


using vi = vector<int>;
using pii = pair<int,int>;

const int inf = INT_MAX / 2;

i32 main() {
	ios::sync_with_stdio(false), cin.tie(nullptr);
	int n, m;
	cin >> n >> m;
	vi a(n + 1);
	for(int i = 1; i <= n; i ++) cin >> a[i];

	vi deg(n + 1);
	for(int x, y; m; m --) {
		cin >> x >> y;
		deg[x] ++, deg[y] ++;
	}

	int leaf1 = -inf, leaf2 = -inf, node = -inf;

	for(int i = 1; i <= n; i ++) {
		if(deg[i] == 1) {
			if(a[i] > leaf1) leaf2 = leaf1, leaf1 = a[i];
			else leaf2 = max(leaf2, a[i]);
		}
		else node = max(node, a[i]);
	}

	if(leaf2 == -inf) cout << node << "\n";
	else if(node == -inf) cout << leaf2 << "\n";
	else cout << max(node, leaf2);

	return 0;
}	

I. 算术

对于任意的两个数\(x,y\),如果满足\(1 < x,y\),则一定有\(xy \ge x + y\)。因为求和操作一定是至少有一个数为\(1\)

这样的话,考虑把卡牌分成若干组,每一组内求和,组之间求积。我们可以给每一组先分配一张卡牌,然后再给某些组进行加一。

因为一个组内不能有两个大于一的数,因此组个数的变化实际上只受到了\(1\)的影响。因此我们可以枚举有多少个\(1\)作为一组。

然后我们考虑,如果\(x < y\),则一定有\((x + 1)y = xy + y > xy + x = x(y + 1)\)。因此加一操作应是对最小的组最优。我们用优先队列维护每组的和,每次对最小的组加一即可。

考虑不同的分组方案如何比较,比较乘积无法实现,但是可以比较乘积的对数。

这样的话,完全没有分类讨论。

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using ldb = long double;

#define int i64

using vi = vector<int>;
using pii = pair<int,int>;

const int mod = 998244353;


void solve() {
	vi a(10);
	for(int i = 1; i <= 9; i ++) cin >> a[i];
	
	vi res;
	ldb val = 0;

	for(int i = 0; i <= a[1]; i ++) {
		priority_queue<int,vi,greater<>> heap;
		for(int j = 0; j < i; j ++)
			heap.push(1);
		for(int j = 2; j <= 9; j ++) 
			for(int k = 0; k < a[j]; k ++)
				heap.push(j);
        if(heap.empty()) continue;


		int x = a[1] - i;
		while(x --) {
			int y = heap.top();
			heap.pop();
			heap.push(y + 1);
		}
		vi ret;
		ldb ans = 0;
		while(not heap.empty()) {
			ret.push_back((i64)heap.top()), ans += log((i64)heap.top());
			heap.pop();
		}
		if(ans > val) res = ret, val = ans;
	}
	int pi = 1;
	for(auto i : res)
		pi = pi * i % mod;
	cout << pi << "\n";
	return 	;
}

i32 main() {
	ios::sync_with_stdio(false), cin.tie(nullptr);

	int T;
	cin >> T;
	while(T --)
		solve();

	return 0;
}	

J. 骰子

观察题目,首先起始状态底面为\(6\)。观察样例,样例证明了存在一种方案可以使得右侧第一格和下边第一个底面为\(6\)。因此一定有一种方法可以使得每一格都是\(6\)

#include <bits/stdc++.h>

using namespace std;


using i32 = int32_t;
using i64 = long long;


using vi = vector<int>;
using pii = pair<int,int>;


i32 main() {
	ios::sync_with_stdio(false), cin.tie(nullptr);
	i64 n, m;
	cin >> n >> m;
	cout << n * m * 6ll << "\n";
	return 0;
}	

K. 小 C 的神秘图形

观察生成图案的方法。如果坐标的最高位都不是\(1\),则为\(0\)。否则可以递归询问。

#include <bits/stdc++.h>

using namespace std;


using i32 = int32_t;
using i64 = long long;


using vi = vector<int>;
using pii = pair<int,int>;

i32 main() {
	ios::sync_with_stdio(false), cin.tie(nullptr);
	int n;
	cin >> n;

	string x, y;
	cin >> x >> y;
	ranges::reverse(x), ranges::reverse(y);

	while(true){
		if((x.back() == '1') or (y.back() == '1')) {
			if(n == 1) {
				cout << 1 << "\n";
				return 0;
			} else {
				x.pop_back(), y.pop_back(), n --;
			}
		}else {
			cout << 0 << "\n";
			return 0;
		}
	}

	return 0;
}	
posted @ 2024-11-13 16:27  PHarr  阅读(259)  评论(0编辑  收藏  举报