第十屆中國大學生程式設計競賽 重慶站(CCPC 2024 Chongqing Site)

PHarr發表於2024-11-13

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

相關文章