CSUACM2024新生赛 - 第0场题解

写在前面

Luckyblock 场。

以后如果你们在什么比赛里做到了这种背景又臭又长的题那大概就是我干的。

A - A*B Problem

签到。

注意开 long long。

B - 合成大大大大数数数数

数学 800

由简单的数学知识可知,从 [1,n] 中不重复地选 k 个数之和的取值范围为:

[i=1ki,i=nk+1ni]

于是仅需判断 x 是否在上述区间内即可。

计算上述区间的左右端点可以直接套用等差数列求和公式,也可以像标准程序一样,使用前缀和快速计算区间的和。

复制复制
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
LL sum[kN];
//=============================================================
inline LL read() {
LL f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3ll) + (w << 1ll) + (ch ^ '0');
return f * w;
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
for (int i = 1; i < kN; ++ i) sum[i] = sum[i - 1] + i;
int T = read();
while (T --) {
int n = read(), k = read(); LL x = read();
LL minx = sum[k], maxx = sum[n] - sum[n - k];
printf("%s\n", minx <= x && x <= maxx ? "YES" : "NO");
}
return 0;
}

C - 我是一个一个签到题

位运算 800

位运算除左右移外,在二进制表示下不进位。

发现两个数进行 or 运算后的值不可能变得更小。于是一个显然的想法是通过若干次操作,将所有数均修改为 a1ora2ororan,这样做答案一定不会变得更劣。

则把所有数全或起来得到的数的 n 倍即为答案。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int n; std::cin >> n;
LL ans = 0;
for (int i = 1; i <= n; ++ i) {
int a; std::cin >> a;
ans |= a;
}
std::cout << ans * n;
return 0;
}

D - 研讨会会计的工作

枚举,贪心 900

对于每一个社团的预算,为了最小化使用的货币数,显然应尽量使用较大面值的货币凑,然后用更小面值的货币补齐剩余部分。发现上述剩余部分即货币除以某种面值的余数,使用这种货币的张数即预算除以面值的商。

于是考虑对于每个社团的预算,将预算依次除以 100、50、10、5、2、1,将商累加即得最少的货币数量。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
int calc(int x_) {
int ret = 0, y = x_;
ret += y / 100; y %= 100;
ret += y / 50; y %= 50;
ret += y / 10; y %= 10;
ret += y / 5; y %= 5;
ret += y / 2; y %= 2;
ret += y;
return ret;
}
//=============================================================
int main() {
std::ios::sync_with_stdio(0), std::cin.tie(0);
int n; std::cin >> n;
LL sum = 0;
for (int i = 1; i <= n; ++ i) {
int x; std::cin >> x;
sum += calc(x);
}
std::cout << sum << "\n";
}
/*
5
1 2 3 4 5
*/

E - 色彩入侵

枚举,贪心 1000

发现颜色种类数很少,于是可以暴力枚举最后数列的形态(即最后整个数列是什么颜色的),然后按顺序枚举数列中的每个位置。发现有颜色与最终形态不同的位置,则贪心地以该位置为左端点进行一次修改即可。

可证明这样贪心地进行修改一定可以使答案最优。

总时间复杂度 O(nv) 级别。

//知识点:枚举,贪心
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#define LL long long
const int kN = 1e5 + 10;
//=============================================================
int n, m, ans, a[kN];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void Chkmax(int &fir, int sec) {
if (sec > fir) fir = sec;
}
void Chkmin(int &fir, int sec) {
if (sec < fir) fir = sec;
}
//=============================================================
int main() {
int t = read();
while (t --) {
n = read(), m = read();
ans = kN;
for (int i = 1; i <= n; ++ i) a[i] = read();
for (int i = 1; i <= 100; ++ i) {
int sum = 0;
for (int j = 1; j <= n; ++ j) {
if (a[j] != i) {
sum ++;
j = j + m -1;
}
}
Chkmin(ans, sum);
}
printf("%d\n", ans);
}
return 0;
}

F - 夜如此寂静

二分答案 1200

发现答案的取值存在单调性——即若某个照亮范围半径 r 是合法的,则所有大于 r 的半径 R 也一定是合法的——于是套路地考虑二分答案枚举 mid,并检查当照亮范围半径 r=mid 时是否合法。

发现若某个确定的 r 合法仅有以下两种情况:

  • OP 均在同一个圆中;
  • O,P 在不同的圆中,且两个圆有交。

直接计算即可检查,单次检查时间复杂度 O(1) 级别。总时间复杂度 O(Tlogv) 级别。


特别地,存在不需要二分答案直接 O(1) 计算的方法。根据上述分析可以考虑讨论实际走的路径的所有情况:

  • OAP
  • OBP
  • OABP
  • OBAP

通过直接计算出上述四种情况的最小半径,再取最小值即为答案。

二分答案:

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const double eps = 1e-12;
//=============================================================
int p[2], a[2], b[2];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
bool Check(double w_) {
double dw = w_;
double da0 = sqrt(1.0 * a[0] * a[0] + 1.0 * a[1] * a[1]);
double db0 = sqrt(1.0 * b[0] * b[0] + 1.0 * b[1] * b[1]);
double dap = sqrt(1.0 * (a[0] - p[0]) * (a[0] - p[0]) + (a[1] - p[1]) * (a[1] - p[1]));
double dbp = sqrt(1.0 * (b[0] - p[0]) * (b[0] - p[0]) + (b[1] - p[1]) * (b[1] - p[1]));
if (da0 > dw && db0 > dw) return false;
if (dap > dw && dbp > dw) return false;
if (da0 <= dw && dap <= dw) return true;
if (db0 <= dw && dbp <= dw) return true;
double dab = sqrt(1.0 * (a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]));
if (dab > 2.0 * dw) return false;
return true;
}
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
int T = read();
while (T --) {
for (int i = 0; i < 2; ++ i) p[i] = read();
for (int i = 0; i < 2; ++ i) a[i] = read();
for (int i = 0; i < 2; ++ i) b[i] = read();
double l = 0, r = 1e10, ans;
while (r - l > eps) {
double mid = (l + r) / 2.0;
if (Check(mid)) {
ans = mid;
r = mid;
} else {
l = mid;
}
}
printf("%.12lf\n", ans);
}
return 0;
}

直接计算:

#include<bits/stdc++.h>
using namespace std;
double xp, yp, xa, ya, xb, yb;
double dispa, dispb, disab;
double disa, disb;
int t;
double get_dist(double x, double y, double x2, double y2)
{
return (double)sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
}
signed main()
{
cin >> t;
while (t -- )
{
cin >> xp >> yp >> xa >> ya >> xb >> yb;
dispa = get_dist(xp, yp, xa, ya);
dispb = get_dist(xp, yp, xb, yb);
disab = get_dist(xa, ya, xb, yb) / 2.0;
disa = get_dist(0, 0, xa, ya);
disb = get_dist(0, 0, xb, yb);
double res = 1e9;
res = min(res, min(max(max(disa, disab), dispb), max(max(disb, disab), dispa)));
res = min(res, min(max(disa, dispa), max(disb, dispb)));
printf("%.10lf\n", res);
}
return 0;
}

G - 明明一直在动但还是好冷啊啊啊

动态规划 1400

fu=0/1(1un) 表示到达节点 u 之后,是否一定可以移动到某个叶节点。对于 fu=1 的节点称它们为必胜点。

显然所有叶节点均为必胜点;对于所有非叶节点 i,当且仅当它有大于 ai 个必胜的子节点 v 时,它才能成为必胜点。即有:

fu={1uleaves[vsonufv>au]otherwise

上式中符号 [] 代表艾佛森括号,有:

[P]={1If P is true0Otherwise

此处 P 是一个可真可假的命题。

当根节点为必胜点时答案为 YES,否则为 NO

通过一次深度优先搜索,在回溯时进行转移实现即可,总时空复杂度均为 O(n) 级别。

//知识点:DP
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e6 + 10;
//=============================================================
int n, a[kN];
bool yes[kN];
std::vector <int> sons[kN];
//=============================================================
void Dfs(int u_) {
int cnt = 0;
for (auto v: sons[u_]) {
Dfs(v);
cnt += yes[v];
}
if (sons[u_].empty()) {
yes[u_] = true;
} else {
yes[u_] = (cnt > a[u_]);
}
}
//=============================================================
int main() {
std::cin >> n;
for (int i = 1; i <= n; ++ i) std::cin >> a[i];
for (int i = 2; i <= n; ++ i) {
int fa; std::cin >> fa;
sons[fa].push_back(i);
}
Dfs(1);
std::cout << (yes[1] ? "YES" : "NO");
return 0;
}

写在最后

没能让大家尽兴真的非常抱歉~

posted @   Luckyblock  阅读(121)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示