AtCoder Beginner Contest 341

A - Print 341 (abc341 A)

题目大意

给定n,输出 n0n+11交替的字符串。

解题思路

101010...循环输出即可。

神奇的代码
n = input()
s = "10" * int(n) + "1"
print(s)


B - Foreign Exchange (abc341 B)

题目大意

货币兑换。

A国货币每 xa钱可兑换 B国货币 ya钱。
B国货币每 xb钱可兑换 C国货币 yb钱。
...

给定你拥有的每国货币钱数和兑换规则,依次兑换,问兑换到最后的国的货币数量。

解题思路

按照题意,按照上述规则一国一国模拟地兑换即可。

神奇的代码
#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(n);
for (auto& x : a)
cin >> x;
for (int i = 0; i < n - 1; ++i) {
int x, y;
cin >> x >> y;
a[i + 1] += a[i] / x * y;
}
cout << a[n - 1] << '\n';
return 0;
}


C - Takahashi Gets Lost (abc341 C)

题目大意

二维平面H×W,有.#。其中#不能占人。

给定一个操作序列T

问高桥的初始位置的数量,使得进过上述操作序列,不会经过#

解题思路

枚举位置,然后模拟操作序列判断是否经过#,时间复杂度为O(HW|T|),为 O(5003),可过。

神奇的代码
#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 h, w, n;
cin >> h >> w >> n;
string t;
cin >> t;
vector<string> s(h);
for (auto& i : s)
cin >> i;
int ans = 0;
auto land = [&](int x, int y) {
return 0 <= x && x < h && 0 <= y && y < w && s[x][y] != '#';
};
auto ok = [&](int x, int y) {
if (!land(x, y))
return false;
int nx, ny;
for (auto& i : t) {
if (i == 'L') {
nx = x;
ny = y - 1;
} else if (i == 'R') {
nx = x;
ny = y + 1;
} else if (i == 'U') {
nx = x - 1;
ny = y;
} else if (i == 'D') {
nx = x + 1;
ny = y;
}
if (!land(nx, ny))
return false;
x = nx;
y = ny;
}
return true;
};
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
if (ok(i, j))
++ans;
}
}
cout << ans << '\n';
return 0;
}


D - Only one of two (abc341 D)

题目大意

给定n,m,k,问从只被n或m整除的数中找出第 k小的数,

解题思路

为方便考虑,可先简化问题,即假设n,m互质。

注意到我们找出来的数是形如 xnxm, 我们考虑枚举这个x

显然枚举出的xn其是第几个数具有单调性,因此我们可以二分枚举这个 x

先考虑xn的形式,我们要判断 xn是第几小的数。

注意到只被n或m整除的数都是形如yn,ym的。

在形如yn类别的数中,xn的排名是 xcost1,其中 cost1yn%m=0y的数量,其中y[1,x]。有多少呢?因为上述假设了n,m互质,那么yn%m=0当且仅当 y%m=0,即cost1=xm

在形如ym类别的数中,xn的排名是 xnmcost2,其中 cost2ym%n=0y的数量,其中y[1,xnm]有多少呢?因为上述假设了n,m互质,那么ym%n=0当且仅当 y%n=0,即cost2=xn/mn

上述的除法均为整除。

那最终xn的排名就是两类别中排名的相加,即为xcost1+xnmcost2。与 k比较,就可以得知该 x是偏大了还是偏小了,可以二分了。

上述是假设了 n,m互质,而如果不互质时,会变得就只有 cost1cost2。假设 n=ngcd(n,m),m=mgcd(n,m)。考虑 cost1如何算。

cost1ygcd(n,m)n%gcd(n,m)m=0y的数量,由于 nm互质,那其数量就等价于 y%m=0y的数量。 即cost1=xm

同理也可以求的 cost2=xn/mn

如此就可以二分求出第x小的数了。

神奇的代码
#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, m;
LL k;
cin >> n >> m >> k;
if (n > m)
swap(n, m);
int gg = gcd(n, m);
LL ans = -1;
auto calc = [&](LL n, LL m, __int128 a) {
__int128 ldi = n / gg;
__int128 rdi = m / gg;
__int128 lcnt = a;
__int128 rcnt = a * n / m;
__int128 cnt = lcnt - (lcnt / rdi) + rcnt - (rcnt / ldi);
return cnt;
};
auto solve = [&](int n, int m) {
__int128 l = 0, r = 1e18;
while (l + 1 < r) {
__int128 mid = (l + r) / 2;
__int128 rank = calc(n, m, mid);
if (rank >= k)
r = mid;
else
l = mid;
}
if (calc(n, m, r) == k && r * n % m != 0) {
ans = r * n;
}
};
solve(n, m);
solve(m, n);
cout << ans << '\n';
return 0;
}


E - Alternating String (abc341 E)

题目大意

给定一个01s,进行以下两种操作:

  • 1 l r,将s[l,r]01翻转
  • 2 l r,问s[l,r]是否是01交替的。

解题思路

一个串是01交替的,就是每一位与下一位的值都不同。

考虑数组 x[i]=(s[i]!=s[i+1]) ,如果i=lr1x[i]==rl,那说明 s[l,r]01交替的。

注意到操作一对该数组的影响仅仅是两个单点修改,而操作二是一个区间查询,用树状数组或线段树维护数组x即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
// starting from 0
template <typename T> class fenwick {
public:
vector<T> fenw;
int n;
fenwick(int _n) : n(_n) { fenw.resize(n); }
void modify(int x, T v) {
while (x < n) {
fenw[x] += v;
x |= (x + 1);
}
}
T get(int x) {
T v{};
while (x >= 0) {
v += fenw[x];
x = (x & (x + 1)) - 1;
}
return v;
}
T sum(int l, int r) {
T tot = get(r);
if (l != 0) {
tot -= get(l - 1);
}
return tot;
}
};
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, q;
cin >> n >> q;
string s;
cin >> s;
fenwick<int> sum(n - 1);
for (int i = 0; i < n - 1; ++i) {
sum.modify(i, s[i] != s[i + 1]);
}
while (q--) {
int op;
cin >> op;
if (op == 1) {
int l, r;
cin >> l >> r;
--l, --r;
if (l != 0) {
sum.modify(l - 1, sum.sum(l - 1, l - 1) == 1 ? -1 : 1);
}
if (r != n - 1) {
sum.modify(r, sum.sum(r, r) == 1 ? -1 : 1);
}
} else {
int l, r;
cin >> l >> r;
--l, --r;
if (sum.sum(l, r - 1) == r - l)
cout << "Yes" << '\n';
else
cout << "No" << '\n';
}
}
return 0;
}


F - Breakdown (abc341 F)

题目大意

给定一张无向图,点有点权wi。一开始有些点有一些碎片ai

问进行的操作的最大次数。

操作为,选择一个有碎片的点x,拿走碎片,并从其邻居中选择一些点,满足wy<wx,在每一个点放一个碎片。

解题思路

注意到操作里,碎片总是往点权小的点跑,这里有个方向性。我们可以先求权值小的点,那考虑权值大的点时,其考虑放置碎片的点的答案都求出来了,因此可以求出该点的答案。考虑dp

按点权小到大的顺序求解,假设dp[i]表示点i有一个碎片带来的最大操作次数,那考虑求解当前点dp[i]时,就是考虑其 wy<wi的所有 y选哪些 y,其 dp[y]最大,且 sumwy<wi,其中这个 dp[y]已经求出来了。

注意到上述的问题就是一个01背包问题,因此就按照点权小到大的顺序求解 n01背包问题即可。

最后答案就是dp[i]×a[i]

考虑其时间复杂度,每次 01背包的复杂度是 O(nw),乍一看以为是 O(n2w),但考虑到每次 01背包中的 O(n)是邻居数量,所有的 01背包的邻居数量的和其实是和 O(m)同数量级的,因此总的时间复杂度是 O(mw)

神奇的代码
#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, m;
cin >> n >> m;
vector<vector<int>> edge(n);
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
--u, --v;
edge[u].push_back(v);
edge[v].push_back(u);
}
vector<int> w(n);
for (auto& x : w)
cin >> x;
int up = *max_element(w.begin(), w.end());
vector<int> a(n);
for (auto& x : a)
cin >> x;
vector<int> id(n);
iota(id.begin(), id.end(), 0);
sort(id.begin(), id.end(), [&](int x, int y) { return w[x] < w[y]; });
vector<int> cost(n);
LL ans = 0;
for (auto i : id) {
vector<array<int, 2>> goods;
for (auto j : edge[i]) {
if (w[j] < w[i])
goods.push_back({w[j], cost[j]});
}
if (goods.empty()) {
cost[i] = 1;
} else {
vector<int> dp(w[i], 0);
for (auto& [x, y] : goods) {
for (int j = w[i] - 1; j >= x; j--) {
dp[j] = max(dp[j], dp[j - x] + y);
}
}
cost[i] = 1 + *max_element(dp.begin(), dp.end());
}
ans += 1ll * a[i] * cost[i];
}
cout << ans << '\n';
return 0;
}


G - Highest Ratio (abc341 G)

题目大意

给定一个数组,对于每一个左端点l,求右端点r,其[l,r]的平均值最大。

解题思路

sum[i]=i=1i为前缀和,区间(l,r]的平均值为sum[r]sum[l]rl

从几何上看这个式子,它就是点(l,sum[l])和点 (r,sum[r])的斜率。

换句话说,二维平面上有一堆点 (i,sum[i]),对于每个题意中的 l,就是要找 il,使得点 (l1,sum[l1])与点 (i,sum[i])的斜率最大。

一个朴素的想法就是从点 (l1,sum[l1])开始,花O(n)求一个凸包 (其实跟最朴素的暴力没区别),复杂度是O(n2)

反过来求凸包,即从最后一个点(n,sum[n])开始往前求凸包,保持斜率增大,当考虑到点 (i,sum[i])时,此时 (i,sum[i]) 下一个点的斜率是最大的,即此时就是对应的ans[i+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<LL> a(n + 1, 0);
for (int i = 1; i <= n; ++i)
cin >> a[i];
vector<LL> sum(n + 1);
partial_sum(a.begin(), a.end(), sum.begin());
vector<pair<LL, LL>> team;
auto convex = [&](const pair<LL, LL>& a, const pair<LL, LL>& b,
const pair<LL, LL>& c) {
return (c.first - a.first) * (b.second - a.second) -
(c.second - a.second) * (b.first - a.first);
};
vector<double> ans(n);
for (int i = n; i >= 0; i--) {
pair<LL, LL> p = {i, sum[i]};
while (team.size() >= 2 &&
convex(team[team.size() - 2], team.back(), p) >= 0) {
team.pop_back();
}
if (team.size() >= 1) {
ans[p.first] = 1.0 * (p.second - team.back().second) /
(p.first - team.back().first);
}
team.push_back(p);
}
cout << fixed << setprecision(8);
for (int i = 0; i < n; ++i) {
cout << ans[i] << '\n';
}
return 0;
}


posted @   ~Lanly~  阅读(595)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示