AtCoder Beginner Contest 324

在高铁上加训!

A - Same (abc324 A)

题目大意

给定n个数,问是否都相等。

解题思路

判断是不是全部数属于第一个数即可。或者直接拿set去重。

神奇的代码
#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<int> a(n);
for (auto& i : a)
cin >> i;
cout << (set<int>(a.begin(), a.end()).size() == 1 ? "Yes" : "No") << '\n';
return 0;
}


B - 3-smooth Numbers (abc324 B)

题目大意

给定一个数N,问是否能表示成 2x3y

解题思路

指数范围不会超过64,花 O(642)枚举判断即可。

神奇的代码
#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);
LL n;
cin >> n;
auto check = [&]() {
for (int i = 0; i < 64; ++i) {
LL x = (1ll << i);
for (int j = 0; j < 64; ++j) {
if (x > n)
break;
if (x == n)
return true;
x *= 3;
}
}
return false;
};
if (check())
cout << "Yes" << '\n';
else
cout << "No" << '\n';
return 0;
}


C - Error Correction (abc324 C)

题目大意

给定一个字符串t和若干个字符串 s。问有哪些 s,满足以下四个条件中的一个:

  • s=t
  • s在某处增加一个字母得到 t
  • s在某处删除一个字母得到 t
  • s在某处修改一个字母得到 t

解题思路

第一个就判断是否逐位相等。

第二个和第三个其实是一样的,就是两者长度是否差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;
string s;
cin >> n >> s;
vector<int> ans;
auto check2 = [](const string& l, const string& r) {
if (r.size() - l.size() != 1)
return false;
int x = 0, y = 0;
while (x < l.size()) {
while (y < r.size() && r[y] != l[x])
++y;
if (y < r.size()) {
++x;
++y;
} else
break;
}
return x == l.size();
};
auto check = [&](const string& t) {
if (s.size() == t.size()) {
int cnt = 0;
for (int i = 0; i < s.size(); ++i)
cnt += (s[i] != t[i]);
return cnt <= 1;
}
if (s.size() < t.size())
return check2(s, t);
else
return check2(t, s);
};
for (int i = 1; i <= n; ++i) {
string t;
cin >> t;
if (check(t))
ans.push_back(i);
}
cout << ans.size() << '\n';
for (auto& i : ans)
cout << i << ' ';
cout << '\n';
return 0;
}


D - Square Permutation (abc324 D)

题目大意

给定一个数字串s,将每一个数位排序,问能排出多少种数,其为平方数。

解题思路

注意到串长度只有13,我们可以枚举每一个平方数x,只有 \(\sqrt{10^{13}\)个,然后判断数字串 s能否表示出这个数x即可。

因为数字串 s可以排列,因此问数字串能否表示出这个数 x,其实就是问这两个的每个数位数字的个数是否相等。

注意计算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;
string s;
cin >> n >> s;
vector<int> cnt(10, 0);
for (auto& i : s)
cnt[i - '0']++;
LL ans = 0;
int up = ceil(sqrt(9999999999999ll));
auto solve = [&](LL x) {
vector<int> num(10, 0);
int sz = 0;
while (x) {
num[x % 10]++;
x /= 10;
++sz;
}
if (sz > n)
return 0;
num[0] += (n - sz);
return int(num == cnt);
};
for (int i = 0; i < up; ++i) {
ans += solve(1ll * i * i);
}
cout << ans << '\n';
return 0;
}


E - Joint Two Strings (abc324 E)

题目大意

给定n个字符串 si和一个字符串 t,长度为m,问有多少组 (i,j),使得拼接串 sisj包含 t这个子序列。

解题思路

我们先枚举si,贪心匹配这个子序列 t,它可能不能完全匹配上,但能匹配 t的前 x位。

那剩下的mx位就交给 sj匹配,如果 sj至少匹配 t的后面mx位,则拼接串sisj 就包含t这个子序列。

因此对于字符串si,它能匹配串 t的前 x位,我们要找出串 sj的数量,可以匹配串 t的至少后mx位,这个数量就是当前串si的贡献。即 (i,)的数量。

而对于字符串sj能匹配 t的后面多少位,其实把它们反串一下贪心匹配即可。

因此就预处理每个串能匹配串t的 前缀位数和后缀位数,然后枚举每个串的匹配的前缀位数,找到要完全匹配t需要的后缀位数,大于该位数的都可以作为j的候选,累计数量即可。因此把后缀位数的个数预处理一下即可。

神奇的代码
#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;
string t;
cin >> n >> t;
int len = t.size() + 1;
vector<int> cnt(len, 0), invcnt(len, 0);
string invt = t;
reverse(invt.begin(), invt.end());
auto check = [](const string& s, const string& t, vector<int>& cnt) {
int l = 0, r = 0;
while (l < s.size()) {
while (r < t.size() && s[l] != t[r])
++r;
if (r < t.size()) {
++l;
++r;
} else
break;
}
cnt[l]++;
};
for (int i = 0; i < n; ++i) {
string s;
cin >> s;
check(t, s, cnt);
reverse(s.begin(), s.end());
check(invt, s, invcnt);
}
LL ans = 0;
reverse(invcnt.begin(), invcnt.end());
partial_sum(invcnt.begin(), invcnt.end(), invcnt.begin());
for (int i = 0; i < len; ++i) {
ans += 1ll * cnt[i] * invcnt[i];
}
cout << ans << '\n';
return 0;
}


F - Beautiful Path (abc324 F)

题目大意

给定一张有向无环图,边有两种边权b,c,求一条从 1n的路径,使得 bc 最大。

解题思路

初想时发现最短路的想法不太行,即时刻保留比值最大的往后扩展并不会最优的,比如此时同样是42,84 ,后者值的变化的程度不如前者,又比如10099,12,后者受b的权值影响更大 ,万一下一条边权是(100,0),显然原先较小的权值会变得更大。

注意到这是一个分式形式,属于典型的0/1规划问题。我们可以二分答案l,判断是否可行,即 bcl,即blc0,即边权为 blc,问是否有条 1n的路径,其边权和非负。

因为是有向无环图,设dp[i]表示到达 点i时的最大边权和 ,拓扑排序dp即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int inf = 2e9;
const double eps = 1e-10;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> n >> m;
vector<array<int, 4>> edges;
vector<vector<int>> G(n);
vector<int> du(n);
vector<int> inque(n);
for (int i = 0; i < m; ++i) {
int u, v, b, c;
cin >> u >> v >> b >> c;
--u, --v;
G[u].push_back(edges.size());
edges.push_back({u, v, b, c});
}
queue<int> team;
team.push(0);
inque[0] = 0;
while (!team.empty()) {
int u = team.front();
team.pop();
for (auto& i : G[u]) {
int v = edges[i][1];
du[v]++;
if (!inque[v])
team.push(v);
inque[v] = 1;
}
}
auto check = [&](double x) {
vector<double> dp(n, -inf);
dp[0] = 0;
team.push(0);
auto dd = du;
auto B = [](int b) { return b; };
auto C = [&x](int c) { return c * x; };
while (!team.empty()) {
int u = team.front();
team.pop();
for (auto& i : G[u]) {
auto [_, v, b, c] = edges[i];
dp[v] = max(dp[v], dp[u] + B(b) - C(c));
--dd[v];
if (dd[v] == 0)
team.push(v);
}
}
return dp[n - 1] >= -eps;
};
double l = 0, r = 2e9;
while (l + eps < r) {
double mid = (l + r) / 2;
if (check(mid))
l = mid;
else
r = mid;
}
cout << fixed << setprecision(12) << l << '\n';
return 0;
}


G - Generate Arrays (abc324 G)

题目大意

给定一个关于n的排列,视为序列0,依次执行 m次操作,分两种:

  • 1sx,将序列s的第 x个元素之后的元素(不包括第 x个)删除,放到一个新序列,相对位置不变。
  • 2sx,将序列s中比x大的元素删除,放到一个新序列,相对位置不变。

问每次操作后,新生成的序列的元素个数。

解题思路

考虑暴力,我们用两个set维护一个序列,一个按下标排序,一个按值排序。这样每个操作都可以在log时间内找到分界点。

如果暴力将要删除的点移到新队列里面,复杂度最高可达 O(nm)

考虑到每次都是将 x个数分成 yxy 个数,如果按照启发式合并的思想(每次将小的合并到大的),我们采用启发式分裂(将数量较少的部分分裂出去),那么复杂度就是O(nlogn)。注意到 setswapO(1)的。

操作一可以直接得到分裂的两个序列的元素个数。

操作二可以先 lower_bound知道对应元素的位置,然后两头指针begin(), end()不断往中间靠,从而知道分裂的两个序列的元素个数大小关系。

神奇的代码
#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<int> a(n);
for (auto& i : a)
cin >> i;
int q;
cin >> q;
vector<set<array<int, 2>>> pos(q + 1), val(q + 1);
for (int i = 0; i < n; ++i) {
pos[0].insert({i, a[i]});
val[0].insert({a[i], i});
}
auto solve = [&](set<array<int, 2>>& l, set<array<int, 2>>& r,
set<array<int, 2>>& ansl, set<array<int, 2>>& ansr,
int x) {
auto it = l.lower_bound({x, 0});
auto lpt = l.begin(), rpt = l.end();
for (; lpt != it && rpt != it; lpt = next(lpt), rpt = prev(rpt))
;
if (rpt == it) {
for (; it != l.end();) {
ansr.insert(r.extract({(*it)[1], (*it)[0]}));
ansl.insert(*it);
it = l.erase(it);
}
} else {
auto node = *it;
for (auto pt = l.begin(); *pt != node;) {
ansr.insert(r.extract({(*pt)[1], (*pt)[0]}));
ansl.insert(*pt);
pt = l.erase(pt);
}
l.swap(ansl);
r.swap(ansr);
}
return ansl.size();
};
for (int i = 1; i <= q; ++i) {
int t, s, x;
cin >> t >> s >> x;
int ans = 0;
if (t == 1) {
int l = x, r = max(0, int(pos[s].size()) - x);
set<array<int, 2>>::iterator it;
if (l <= r) {
for (it = pos[s].begin(); l > 0; it = next(it), l -= 1)
;
} else {
for (it = pos[s].end(); r > 0; it = prev(it), r -= 1)
;
}
if (it == pos[s].end())
x = n;
else
x = (*it)[0];
ans = solve(pos[s], val[s], pos[i], val[i], x);
} else {
ans = solve(val[s], pos[s], val[i], pos[i], x + 1);
}
cout << ans << '\n';
}
return 0;
}


posted @   ~Lanly~  阅读(334)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示