GYM 101522 La Salle-Pui Ching Programming Challenge 培正喇沙編程挑戰賽 2017

C.Cheering

题意:

判断一个字符串中LSC, PCMS 哪个字符穿出现的次数多,如果一样多输出Tie

思路:

模拟就行

signed main() { std::string s; cin >> s; int res = 0, ret = 0; std::string A = "LSC", B = "PCMS"; int N = s.size(); for (int i = 0; i <= N - 3; i++) { if (s.substr(i, 3) == A) res++; } for (int i = 0; i <= N - 4; i++) { if (s.substr(i, 4) == B) ret++; } if (res == ret) puts("Tie"); else puts(res > ret ? "LSC" : "PCMS"); return 0; }

A.Ambiguous Dates

题意:

一共有 N 月,每一月有 Di 天,要求对 D[1N] 重排序,使得最后日和月可以互换的合法天数最少。

思路:

因为月是从小到大递增的,应该尽可能的让第 i 月的 Di 小,这样就可以防止 Dii日的日期合法。故将 Di 从小到大排个序然后统计就好了。

signed main() { int n; cin >> n; vector<int> d(n + 1); rep(i,1,n + 1) cin >> d[i]; sort(d.begin(), d.end()); long long ans = 0; rep(i,1, n + 1) if (d[i] > i) ans += min<long long>(n - i, d[i] - i); printf("%lld\n", ans * 2); return 0; }

B. Bacteria Experiment

题意:

给一个无向树,每一次可以让 u,v 相连边(当且仅当 u>x>v ), 求出多少次操作后可以让这棵树变成完全图。

思路:

首先假设这是一条链,现在模拟一下整个的流程



深度为 5 需要三次操作。由于每一次能够连的点的数量增长的十分快,所以应该去看深度而不是点数的变化,第一次深度为 1 的点可以连向深度为 3 的, 第二次深度为 1 的就可以连向深度为 45 的, 再下一次就可以连向深度为 6789 的点,不难看出每一次都是在上一次的基础上 ×2 ,所以只需要知道树的直径的最高二进制位是多少。

const int N = 500005; int h[N], idx; struct node { int v, nxt; }e[N * 2]; void Add(int u, int v) { e[++idx] = {v, h[u]}, h[u] = idx; } int dep[N]; void dfs(int u, int fa) { dep[u] = dep[fa] + 1; for (int i = h[u]; i; i = e[i].nxt) { int v = e[i].v; if (v == fa) continue; dfs(v, u); } } signed main() { int n; cin >> n; rep(i, 1, n) { int u, v; cin >> u >> v; Add(u, v); Add(v, u); } dfs(1, 0); int rt1 = 0, mx = 0; for (int i = 1; i <= n; i++) { if (dep[i] > mx) mx = dep[i], rt1 = i; } memset(dep, 0, sizeof(int) * (n + 1)); dfs(rt1, 0); mx = *max_element(dep + 1, dep + 1 + n); mx--; int ans = 0, p = 1; while(p < mx) ans++, p *= 2; printf("%d\n", ans); return 0; }

D. Distribution of Days

题意:

求出 SE 年间,MD 日出现在一周七天内分别出现了多少次。

思路:

由于闰年是 4 年一闰 100 年不闰,400 年闰,所以可以将年分成 400 为一个循环,那么这 400 年中一共有 146097 天,而 7146097 所以只需要用 400 作为一个循环节就可以了,预处理出来 2000 年到 2400M.D 出现在周一到周日的次数就行了。

int day[1000][13][40]; int a[13]; int check(int x) { if ((x % 400 == 0) || (x % 4 == 0 && x % 100 != 0)) return true; return false; } signed main() { rep(i, 1, 13) { if (i == 2) a[i] = 28; else if (i <= 7) a[i] = 30 + (i & 1); else if (i >= 8) a[i] = 30 + (i & 1 ^ 1); } int idx = 6; rep(i, 0, 400) { rep(j, 1, 13) { if (j == 2) { for (int k = 1; k <= a[j] + check(i); k++) { idx %= 7; day[i][j][k] = ++idx, day[i + 400][j][k] = idx; } } else { for (int k = 1; k <= a[j]; k++) { idx %= 7; day[i][j][k] = ++idx, day[i + 400][j][k] = idx; } } } } int q; cin >> q; while(q--) { int s, e, m, d; cin >> s >> e >> m >> d; array<long long, 8> ans{}; if (e - s >= 400) { int p = (e - s) / 400; for (int i = s % 400; i <= s % 400 + 399; i++) ans[day[i][m][d]] += p; } e %= 400, s %= 400; if (e < s) e += 400; for (int i = s; i <= e; i++) ans[day[i][m][d]]++; rep(i, 1, 8) printf("%lld%c", ans[i], " \n"[i == 7]); } return 0; }

E. Expected Score

题意:

一共有 N 个数,2×N 个箱子,每一个数出现在两个不同箱子上面。每一轮游戏取出两个箱子,如果这两个箱子上面的数字相同,就将这个数字该轮游戏的得分, 否则得分为0,最后将箱子放回原位。现在一共有 R 轮游戏,已经进行了 R1 轮,求出最后一轮可以得到该轮游戏最高分的期望是多少。

思路:

由于我们可以知道前面 R1 轮取出的箱子他们表示的数是多少,所以我们可以把 N 个数分成三种情况来讨论,第一种是 i 在不同位置的箱子上出现了两次,第二种是 i 在已知的箱子中只有一个表示 i, 第三种是 i 在已知的箱子中没有出现。将数分成了三类,那么将答案也分三种情况讨论,将 R1 轮最高分计为 res, 第 R 轮的得分记为 ret, 未知的箱子数量记为 M
第一种情况:在已知的箱子里面选出两个箱子作为最后的得分,很显然你一定是在 res+1N 中选择两个数,否则答案一定不是最优的,那么这种情况的期望就是 ret
第二种情况:在已知的箱子中选择一个箱子在未知的箱子中选择一个,那么一定是 ret 在已知的箱子中只出现了一次,所以在 M 个箱子中只有一个箱子可能出现 ret, 那么最后我们得到 ret 的概率是 1M, 而得到 res 的概率是 M1M ,所以这种情况的期望是 ret×1M+res×M1M
第三种情况:在未知的箱子中选择两个数,选到 ret 的概率是 1M×(M1) ,选到 res 的概率是 1i=res+1Ni×1M×(M1)(iunkown) 所以最后的期望也就求出了。
最终的答案就是三种情况的期望取最大值

signed main() { int n, r; cin >> n >> r; std::vector<int> cnt(n + 1), used(n * 2 + 1); int cur = 0; int M = 0; rep(i, 1, r) { int x, y, cx, cy; cin >> x >> y >> cx >> cy; if (!used[x]) used[x] = 1, M++, cnt[cx]++; if (!used[y]) used[y] = 1, M++, cnt[cy]++; if (cx == cy) cur = max(cur, cx); } M = 2 * n - M; // total of unopened boxes int res = 0; // case1 : both opened for (int i = 1; i <= n; i++) { if (cnt[i] == 2) res = i; } res = max(res, cur); double ret = 0; std::vector<int> open, unopen; rep(i, 1, n + 1) { if (cnt[i] == 1 && cur < i) open.push_back(i); else if (!cnt[i] && cur < i) unopen.push_back(i); } ret = max<double>(ret, res); // case2 : one open one unopen for (auto& x : open) ret = max<double>(ret, x * 1.0 / M + cur * (M - 1.0) / M); //case3 : both unopen double ans = 0, now = 0; for (auto& x : unopen) { ans += 2.0 / M / (M - 1) * x; now += 2.0 / M / (M - 1); } ans += (1.0 - now) * cur; ret = max<double>(ans, ret); printf("%.10lf\n", ret); return 0; }

F. Frustrating Game

题意:

N 个排成一行的仅由 RB 组成的球,可以使用魔法将 r个红球和 b 个蓝球消掉变成白球,但是这些球中间不能够有白球。

思路:

如果r=1&&b=1 那么就可以将RB 串看作是一个括号序列,让我们求出该匹配方案,这就只需要用一个栈来作括号序列的匹配就可以了。那么类比到一般的情况,就每次判断在栈顶的 r+b 的元素中是不是有 rR bB,是的话就删掉这些数,最后将方案倒着输出一遍就行。

int stk[100005], top; std::string s; int n, r, b; int sum[100005][2]; signed main() { cin >> n >> r >> b; cin >> s; int R = 0, B = 0; for (auto& ch : s) R += (ch == 'R'), B += (ch == 'B'); if (R % r || B % b || (R / r != B / b)) { puts("NO"); return 0; } puts("YES"); vector<vector<int>> ans; int res = 0, ret = 0; for (int i = 0; i < static_cast<int>(s.size()); i++) { res += s[i] == 'B'; ret += s[i] == 'R'; sum[i][0] = res; sum[i][1] = ret; stk[++top] = i; if (top > r + b) { int p = stk[top - r - b]; if (res - sum[p][0] == b && ret - sum[p][1] == r) { vector<int> cur; for (int j = 1; j <= r + b; j++) { res -= s[stk[top]] == 'B'; ret -= s[stk[top]] == 'R'; cur.push_back(stk[top]); top--; } reverse(cur.begin(), cur.end()); ans.emplace_back(cur); } } } if (top) { assert(top == r + b); vector<int> cur; for (int i = 1; i <= top; i++) { cur.push_back(stk[i]); } ans.emplace_back(cur); } reverse(ans.begin(), ans.end()); printf("%d\n", static_cast<int>(ans.size())); for (auto& vec : ans) { for (auto& x : vec) { printf("%d ", x + 1); } puts(""); } return 0; }

G. Gravitational Tetris

题意:

8 种积木,且可以自由下落,构造一组方案使得最后, 10 列拥有一样的行数。

思路:

可以将所有前 9 列的格子都拼到 40 行,最后一列只有可能是 cnt%4=2 or 0 这样才是有解的,前面 9 列的拼法就是 hi+4x+3y=40y3, 采用 A,G 两种积木来拼,同时需要去更新 ai+1, 因为 G 种积木他向右伸出了一格,最后一列特判一下就行了,如果是 cnt%4=2 的情况需要用一个 E 种积木和两个 B 来调整所有列变成 41 行。

int h[11]; int cnt[10]; signed main() { rep(i, 0, 10) { cin >> h[i]; } string s; vector<int> ret; int sum = 0; rep(i, 0, 9) { int d = 40 - h[i]; int p = d / 4; int a = 0, g = 0; for (int j = p; j >= 0; j--) { if ((40 - h[i] - 4 * j) % 3 == 0) { a = j, g = (40 - h[i] - 4 * j) / 3; break; } } h[i + 1] += g; h[i] = 40; rep(j, 0, a) s += 'A', ret.push_back(i + 1); rep(j, 0, g) s += 'G', ret.push_back(i + 1); sum += a + g; } if (h[9] % 4 == 2) { for (int i = 0; i < (38 - h[9]) / 4; i++) { sum++; s += 'A'; ret.push_back(10); } s += 'E', ret.push_back(9); s += 'B', ret.push_back(1), s += 'B', ret.push_back(5); sum += 3; } else if (h[9] % 4 == 0) { for (int i = 0; i < (40 - h[9]) / 4; i++) { s += 'A'; ret.push_back(10); sum++; } } printf("%d\n", sum); for (int i = 0; i < s.size(); i++) { printf("%c %d\n", s[i], ret[i]); } return 0; }

H. Hit!

题意:

求出两圆任意一个公共点

思路:

计算几何板子,也可以用定比例分点、

using namespace Geometry; int main() { std::cin.tie(nullptr)->sync_with_stdio(false); std::cout << std::fixed << std::setprecision(10); Circle p1, p2; cin >> p1.p >> p1.r; cin >> p2.p >> p2.r; if (intersect(p1, p2) > 1) { //相交或外切 auto ret = crosspoint(p1, p2); Point res = ret.first + ret.second; cout << res.real() / 2 << " " << res.imag() / 2 << "\n"; } else { //内切或者内含 if (p1.r > p2.r) std::cout << p2.p << "\n"; else std::cout << p1.p << "\n"; } }

I. Inverted Signs

题意:

可以改变 [lr] 区间内所有数变成他们的相反数,求出最后整个序列 i=1n1|hihi+1| 的最小值。

思路:

因为区间内改变正负性,不会影响这个区间对答案的贡献,所以只需要计算出相邻两项变化值是多少,最后取最小的两个负数加到原来的答案上就可以了

signed main() { int n; cin >> n; long long ans = 0; vector<int> a(n); for (int i = 0; i < n; i++) { cin >> a[i]; } vector<long long> res(n); for (int i = 1; i < n; i++) { ans += abs(a[i] - a[i - 1]); } for (int i = 0; i < n - 1; i++ ) { res[i] = abs(-a[i + 1] - a[i]) - abs(a[i + 1] - a[i]); } sort(res.begin(), res.end()); if (res[0] < 0) ans += res[0]; if (res[1] < 0) ans += res[1]; printf("%lld\n", ans); return 0; }

J. Juicy Candies

题意:

bB, rR, sS, 且相邻的不能相同,求字典序第 k 小的方案。

思路:

求出字典序第 k 小也就是要求出对应的一个前缀,计算这个前缀的方法可以只关心最后一个字符和前面一共有多少个 RBS, 这样我们可以转化成求出求一个后缀,这样就只需要关心这个后缀中有多少个RBS 和第一个字符是什么。这样就可以用一个 dp 来转移方案数,定义 dp[i][b][s][r] 表示现在用了bB, rR, sS, 且在最开头是 i{0,1,2} 的方案有多少种,转移方程也很容易想到就是从另外两个状态转移过来, 但是如果直接计算方案数的话,可能会爆 long long, 我们只需要关心前 k 个的方案,所以后面的就不需要去关心,所以最后 dp[i][b][r][s] 都要和 k+1 取个最小值

long long dp[210][210][210][3]; signed main() { int b, r, s; long long k; cin >> b >> r >> s >> k; std::string ans; dp[1][0][0][0] = dp[0][1][0][1] = dp[0][0][1][2] = 1; rep(i, 0, b + 1) rep(j, 0, r + 1) rep(p, 0, s + 1) rep(c, 0, 3) { if (c == 0 && i) dp[i][j][p][c] += dp[i - 1][j][p][1] + dp[i - 1][j][p][2]; if (c == 1 && j) dp[i][j][p][c] += dp[i][j - 1][p][2] + dp[i][j - 1][p][0]; if (c == 2 && p) dp[i][j][p][c] += dp[i][j][p - 1][1] + dp[i][j][p - 1][0]; dp[i][j][p][c] = min(dp[i][j][p][c], k + 1); } long long sum = 0; for (int i = 0; i < 3; i++) sum += dp[b][r][s][i]; if (sum < k) { puts("None"); return 0; } int N = b + r + s; int pre = -1; for (int i = 1; i <= N; i++) { for (int j = 0; j < 3; j++) if (pre ^ j) { if (dp[b][r][s][j] >= k) { if (b && j == 0) b--, ans += 'B'; if (r && j == 1) r--, ans += 'R'; if (s && j == 2) s--, ans += 'S'; pre = j; break; } else { k -= dp[b][r][s][j]; } } } puts(ans.c_str()); return 0; }

K. Knights

题意:

N×M矩形,同行或同列已被占领的格子中间的所有格子都会被占领,求在给定已占领格子的基础上占领所有格子还至少需要派骑士占领多少个格子

思路:

判断四个角就行,另外特判 1×1, N×1, 1×M 的情况

signed main() { int n, m, k; cin >> n >> m >> k; int ans = 4; std::set<std::pair<int, int>> S; rep(i, 0, k) { int x, y; cin >> x >> y; S.insert(make_pair(x, y)); } if (n == m && n == 1) { ans = 1; ans -= S.count(make_pair(1, 1)); } else if (n == 1 || m == 1) { ans = 2; ans -= S.count(make_pair(1, 1)); ans -= S.count(make_pair(n, m)); } else { ans -= S.count(make_pair(1, 1)); ans -= S.count(make_pair(n, 1)); ans -= S.count(make_pair(1, m)); ans -= S.count(make_pair(n, m)); } printf("%d\n", max(0, ans)); return 0; }

L. Let Me Count The Ways

题意:

给定两行,分别 n 列,m(nm), 将1n+m个数放入这 n+m个格子,使得数字不重复、每行前面的数比后面的数大,每列第一行的数比第二行的数大,求有几种放法。对 109+7 取模

思路:

是一个标准杨表的问题,答案就是卡特兰数 (n+mn)(n+mn+1) 证明参考:L题解
但是这个数太大了 N,M109 肯定是不可以直接算出所有的 1109 阶乘和逆元的,所以考虑Lucas 定理,确实可以解决这个问题,但是在第五个样例的测试的时候要跑 22s 显然不能够通过本题,那么就要使用分段打表的技巧,预处理出来 1000000!,2000000!,,109!, 这样算任意一个数的阶乘的时候最多只需要算 1000000 次就可以算出来了,而且大于 109+7 的数的阶乘不需要去计算答案肯定是 0

const int B = 1e6; Mint fac[B] = {...}; //预处理出来的阶乘 Mint calc(long long x) { Mint res = fac[x / B]; for (int i = x / B * B + 1; i <= x; i++) res *= i; return res; } signed main() { long long n, m; cin >> n >> m; if (n + m >= P) return printf("0\n"), 0; else { Mint a = calc(n + m); Mint b = calc(m - 1), c = calc(n + 1), d = calc(m), e = calc(n); printf("%d\n", (a / d / e - a / b / c)()); } return 0; }

__EOF__

本文作者HoneyGrey
本文链接https://www.cnblogs.com/Haven-/p/17053368.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   浅渊  阅读(42)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示