2023-12-21 15:36阅读: 283评论: 0推荐: 0

AtCoder Beginner Contest 333

A - Three Threes (abc333 A)

题目大意

给定一个09的数n,输出这个数 n次。

解题思路

模拟即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int a;
cin >> a;
cout << string(a, '0' + a) << '\n';
return 0;
}


B - Pentagon (abc333 B)

题目大意

给定一个五边形和两个由两个顶点组成的线段,问线段长度是否相等。

解题思路

由全等可得比较两线段的长度等价于比较其端点在边上的距离相等。

于是给每个顶点编号,计算下一条线段的两个端点的距离(有两个,顺时针和逆时针,取较小的),然后判断这个距离是否相等即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
string s1, s2;
cin >> s1 >> s2;
map<char, int> pos;
for (int i = 0; i < 5; ++i) {
pos['A' + i] = i;
}
auto dis = [&](char a, char b) {
int dis = abs(pos[a] - pos[b]);
return min(dis, abs(5 - dis));
};
if (dis(s1[0], s1[1]) == dis(s2[0], s2[1]))
cout << "Yes" << '\n';
else
cout << "No" << '\n';
return 0;
}


C - Repunit Trio (abc333 C)

题目大意

定义一类数,其数位都是1,即 1,11,111,1111,...

问第 n小的数,它可以表示成三个上述数的和。

解题思路

范围只有333,直接枚举所有的组合情况,超过333break

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<LL> a;
set<LL> ans;
for (LL i = 1; 1; i = i * 10 + 1) {
a.push_back(i);
for (auto& j : a)
for (auto& k : a) {
ans.insert(i + j + k);
}
if (ans.size() >= n)
break;
}
cout << vector<LL>(ans.begin(), ans.end())[n - 1] << '\n';
return 0;
}


D - Erase Leaves (abc333 D)

题目大意

给定一棵树,每次删去一个叶子。

问把1号点删除的最小操作数。

解题思路

考虑1号点的所有儿子,当仅剩一个儿子时才可以删去 1号点,那就保留儿子子树最大的那棵,其余全删除即可。

DFS预处理下每个子树的大小和最大的儿子子树大小。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<vector<int>> edge(n);
for (int i = 0; i < n - 1; ++i) {
int u, v;
cin >> u >> v;
--u, --v;
edge[u].push_back(v);
edge[v].push_back(u);
}
vector<int> son(n), maxson(n);
auto dfs = [&](auto self, int u, int fa) -> void {
maxson[u] = son[u] = 1;
for (auto& v : edge[u]) {
if (v == fa)
continue;
self(self, v, u);
maxson[u] = max(maxson[u], son[v]);
son[u] += son[v];
}
};
dfs(dfs, 0, 0);
cout << son[0] - maxson[0] << '\n';
return 0;
}


E - Takahashi Quest (abc333 E)

题目大意

冒险,有背包,背包有容量。有两种事件:

  • 遇到一瓶种类为x的毒药,可以选择捡到背包里或不捡
  • 遇到一个种类为x的怪兽,如果没有对应的毒药,则寄了。否则消耗一瓶。

依次给出 n个事件,问能否活到最后,如果能活到,问背包容量的最小值。并给出对应的第一类事件的行为。

解题思路

类似于小车加油的问题,遇到一瓶毒药时,先不决定是否捡,而是作为一种后备选项,直到遇到对应的怪兽时,再决定当初要不要捡。

为了使背包容量最小,当这类毒药有多个时间点可以捡的时候,我们肯定是选择最近的那个时刻l来捡,直到当前时刻r用掉。选更早时刻的不会更优。

决策是上述的贪心做法,剩下的就是维护背包的容量,如果 时刻l捡一个物品,到时刻 r用掉,那么时刻 [l,r1]的背包容量都会 +1。由于要多次区间加法,仅最后一次查区间最值,这里可以用差分数组维护。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<vector<array<int, 2>>> potion(n);
vector<int> bag(n + 1);
vector<int> action;
bool ok = true;
for (int i = 0; i < n; ++i) {
int t, x;
cin >> t >> x;
--x;
if (t == 1) {
potion[x].push_back({int(action.size()), i});
action.push_back(0);
} else {
if (potion[x].empty()) {
ok = false;
break;
} else {
auto [potion_pos, pos] = potion[x].back();
potion[x].pop_back();
action[potion_pos] = 1;
bag[pos]++;
bag[i + 1]--;
}
}
}
int ans = bag[0];
for (int i = 1; i < n; ++i) {
bag[i] += bag[i - 1];
ans = max(ans, bag[i]);
}
if (!ok) {
cout << -1 << '\n';
} else {
cout << ans << '\n';
for (auto& i : action)
cout << i << ' ';
cout << '\n';
}
return 0;
}


F - Bomb Game 2 (abc333 F)

题目大意

n个人排队。每个时刻两个事件等概率发生:

  • 第一个人出队
  • 第一个人去该队伍的最后的位置。

最后剩下一个人。

问每个人存活到最后的概率。

解题思路

dp[i][j]表示有 i个人时,第 j个人存活的概率。根据概率的定义,有

  • dp[i][1]=12dp[i][i]
  • dp[i][j]=12dp[i1][j1]+12dp[i][j1]

很显然会发现有循环依赖,粗暴点可以直接高斯消元,但这题n3000跑不动 O(n3)

那就把循环依赖去掉,去掉的方法就是把式子展开,然后移项即可。

dp[i][1]=12idp[i][1]+j=1i112j+1dp[i1][ij]

dp[i][1]=j=1i12ij1dp[i1][ij]2i1

求出了dp[i][1]后, dp[i][2],dp[i][3]都可以顺势算出来了。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int mo = 998244353;
long long qpower(long long a, long long b) {
long long qwq = 1;
while (b) {
if (b & 1)
qwq = qwq * a % mo;
a = a * a % mo;
b >>= 1;
}
return qwq;
}
long long inv(long long x) {
if (x < 0)
x += mo;
return qpower(x, mo - 2);
}
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<int> dp{0, 1};
int inv2 = inv(2);
for (int i = 2; i <= n; ++i) {
vector<int> dp2(n + 1);
int p2 = 1;
for (int j = i - 1; j >= 1; --j) {
dp2[1] += 1ll * dp[i - j] * p2 % mo;
if (dp2[1] >= mo)
dp2[1] -= mo;
p2 = 1ll * p2 * 2 % mo;
}
dp2[1] = 1ll * dp2[1] * inv(qpower(2, i) - 1) % mo;
for (int j = 2; j <= i; ++j) {
dp2[j] = 1ll * (dp[j - 1] + dp2[j - 1]) * inv2 % mo;
}
dp.swap(dp2);
}
for (int i = 1; i <= n; ++i)
cout << dp[i] << " \n"[i == n];
return 0;
}


G - Nearest Fraction (abc333 G)

题目大意

给定r,n,找一对 p,q,使得 0p,qngcd(p,q)==1,且 |rpq|最小。

解题思路

考虑到糖水加糖,越加越甜。对于初始范围[0n,n0] ,可以二分中间值0+nn+0(分子分母分别取均值),然后判断 r在哪个范围内。然后继续二分。

但貌似 atcoderc++long double的精度不够。

python竟然有直接求解的库,偷了。

减了个1e-100是想避免出现多个最小值。limit_denominator是输出分母最小的那个,而原题要求pq最小。

神奇的代码
from fractions import Fraction
fr = Fraction(input())
n = int(input())
print(*(fr - Fraction("1e-100")).limit_denominator(n).as_integer_ratio())


本文作者:~Lanly~

本文链接:https://www.cnblogs.com/Lanly/p/17919200.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   ~Lanly~  阅读(283)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.