第十届中国大学生程序设计竞赛 重庆站(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;
}