AtCoder Beginner Contest 257
咕咕咕咕咕。
F - Teleporter Setting
题意
给一个有个节点条边的无向图,边集中有一部分边只确定了一端,记这部分边的集合为。
对于,问当中的边的不确定的那一端均为时,从点到点的最短路长度。
其中,。
思路
新增一个虚拟节点,将中的边的不确定的那一段都看成是。
在询问时,可以看成是在和之间连一条长度为的边。
此时的最短路,要么不包含中的边,要么是或。
然后就是dijkstra板子了。
AC代码
// Problem: F - Teleporter Setting
// Contest: AtCoder - NS Solutions Corporation Programming Contest 2022(AtCoder Beginner Contest
// 257)
// URL: https://atcoder.jp/contests/abc257/tasks/abc257_f
// Memory Limit: 1024 MB Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)
#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif
using i64 = int64_t;
using u64 = uint64_t;
void solve_case(int Case);
int main(int argc, char* argv[]) {
CPPIO;
int T = 1;
// std::cin >> T;
for (int t = 1; t <= T; ++t) {
solve_case(t);
}
return 0;
}
template <typename DistanceType>
struct Edge {
int v_;
DistanceType w_;
Edge() {}
Edge(int v, DistanceType w) : v_(v), w_(w) {}
};
template <typename DistanceType>
std::vector<DistanceType> Dijkstra(const std::vector<std::vector<Edge<DistanceType>>>& g, int s) {
using Node = std::pair<DistanceType, int>;
const DistanceType INF = std::numeric_limits<DistanceType>::max();
const int n = g.size();
std::vector<DistanceType> dis(n, INF);
std::vector<bool> vis(n, false);
std::priority_queue<Node, std::vector<Node>, std::greater<Node>> q;
dis[s] = 0;
q.push(Node(dis[s], s));
while (!q.empty()) {
auto [c, u] = q.top();
q.pop();
if (vis[u])
continue;
vis[u] = true;
for (auto [v, w] : g[u]) {
if (dis[v] > c + w) {
dis[v] = c + w;
q.push(Node(dis[v], v));
}
}
}
return dis;
}
void solve_case(int Case) {
int n, m;
std::cin >> n >> m;
std::vector<std::vector<Edge<int>>> g(n + 1);
for (int i = 1; i <= m; ++i) {
int u, v;
std::cin >> u >> v;
if (u == 0) {
u = n + 1;
}
--u, --v;
g[u].push_back(Edge(v, 1));
g[v].push_back(Edge(u, 1));
}
std::vector<int> ds = Dijkstra(g, 0);
std::vector<int> dt = Dijkstra(g, n - 1);
const int INF = std::numeric_limits<int>::max();
for (int i = 0; i < n; ++i) {
int mi = ds[n - 1];
if (ds[n] != INF && dt[i] != INF)
mi = std::min(mi, ds[n] + dt[i]);
if (ds[i] != INF && dt[n] != INF)
mi = std::min(mi, ds[i] + dt[n]);
if (mi == INF)
mi = -1;
std::cout << mi << " ";
}
}
G - Prefix Concatenation
题意
给两个字符串和,问最少用多个的前缀能拼出。
其中。
思路
记表示最少用多少个的前缀能够拼出长度为的前缀。
然后猜了个结论,就是把令,然后跑kmp求出每个位置的最长border长度记为数组,则只从处转移即可。
AC代码
// Problem: G - Prefix Concatenation
// Contest: AtCoder - NS Solutions Corporation Programming Contest 2022(AtCoder Beginner Contest
// 257)
// URL: https://atcoder.jp/contests/abc257/tasks/abc257_g
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)
#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif
using i64 = int64_t;
using u64 = uint64_t;
void solve_case(int Case);
int main(int argc, char* argv[]) {
CPPIO;
int T = 1;
// std::cin >> T;
for (int t = 1; t <= T; ++t) {
solve_case(t);
}
return 0;
}
std::vector<int> kmp(const std::string& s) {
int n = s.size();
std::vector<int> p(n);
p[0] = 0;
for (int i = 1; i < n; ++i) {
int j = p[i - 1];
while (j > 0 && s[j] != s[i])
j = p[j - 1];
if (s[i] == s[j])
++j;
p[i] = j;
}
return p;
}
void solve_case(int Case) {
std::string s;
std::string t;
std::cin >> s;
std::cin >> t;
int n = s.size(), m = t.size();
s = s + "#" + t;
auto p = kmp(s);
std::vector<int> dp(m + 1, 0x3f3f3f3f);
dp[0] = 0;
for (int i = 1; i <= m; ++i) {
int l = p[n + i];
dp[i] = std::min(dp[i], dp[i - l] + 1);
}
if (dp[m] == 0x3f3f3f3f)
dp[m] = -1;
std::cout << dp[m] << "\n";
}
Ex - Dice Sum 2
题意
给定 个六面骰子,第 个骰子的第 面上面的数字为 ,第 个骰子值 块钱。
要从这 个骰子中选择 个使得 取值的期望最大,其中 为所选骰子每个骰子摇一次,显示数字之和的平方, 为所选骰子的价值之和。
其中 。
思路
问题转化
假设所选的骰子集合为,根据期望的定义,有
令,则有
令,则有
把一个选择方案 看成二位平面上的一个点 ,现在问题转化为给定点集,要求最大化。
问题求解
对于最优的选择方案 ,一定存在某个实数 使得。
证明
考虑在某条线段上恒速运动的点,的图像是关于 的一个开口向上的抛物线,由此其极值点必定在两个端点之一。拓展到多个点时,则有极值点必定位于点集的凸包的顶点。
对于每一个顶点,都有某一个 ,使得 最大化。
对于某一个 ,也可以找到对应的一个顶点使得 最大化,这个顶点对应的选择方案为选择 前 大的点。
求解
考虑从小到大搜索 的值,复杂度爆炸。
上述过程可以分成多段结果相同的段。特别地,如果把 按 排序,只有在 在 时,或者说时,结果会发生改变,改变为 和 交换位置,原本位于 中间的点保持不变。
易得只有个使结果发生变化的 ,每对点也至多交换一次。
不妨先令 ,然后再不断降低 的取值,从而搜索所有可能的交换,单次交换至多交换两个点。
时间复杂度为 ,瓶颈在于快排。
补充
求解部分kmjp's blog这个博客讲的比官方题解具体。
AC代码
// Problem: Ex - Dice Sum 2
// Contest: AtCoder - NS Solutions Corporation Programming Contest 2022(AtCoder Beginner Contest
// 257) URL: https://atcoder.jp/contests/abc257/tasks/abc257_h Memory Limit: 1024 MB Time Limit:
// 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#define ASSERT(x) ;
#define serialize() ""
#endif
using i64 = int64_t;
using u64 = uint64_t;
void Initialize();
void SolveCase(int Case);
int main(int argc, char* argv[]) {
CPPIO;
int T = 1;
// std::cin >> T;
for (int t = 1; t <= T; ++t) {
SolveCase(t);
}
return 0;
}
void Initialize() {}
template <typename ValueType, ValueType mod_, typename SupperType>
class Modular {
static ValueType normalize(ValueType value) {
if (value >= 0 && value < mod_)
return value;
value %= mod_;
if (value < 0)
value += mod_;
return value;
}
static ValueType power(ValueType value, size_t exponent) {
ValueType result = 1;
ValueType base = value;
while (exponent) {
if (exponent & 1)
result = SupperType(result) * base % mod_;
base = SupperType(base) * base % mod_;
exponent >>= 1;
}
return result;
}
public:
Modular(ValueType value = 0) : value_(normalize(value)) {}
Modular(SupperType value) : value_(normalize(value % mod_)) {}
ValueType value() const { return value_; }
Modular inv() const { return Modular(power(value_, mod_ - 2)); }
Modular power(size_t exponent) const { return Modular(power(value_, exponent)); }
friend Modular operator+(const Modular& lhs, const Modular& rhs) {
ValueType result = lhs.value() + rhs.value() >= mod_ ? lhs.value() + rhs.value() - mod_
: lhs.value() + rhs.value();
return Modular(result);
}
friend Modular operator-(const Modular& lhs, const Modular& rhs) {
ValueType result = lhs.value() - rhs.value() < 0 ? lhs.value() - rhs.value() + mod_
: lhs.value() - rhs.value();
return Modular(result);
}
friend Modular operator-(const Modular& lhs) {
ValueType result = normalize(-lhs.value() + mod_);
return result;
}
friend Modular operator*(const Modular& lhs, const Modular& rhs) {
ValueType result = SupperType(1) * lhs.value() * rhs.value() % mod_;
return Modular(result);
}
friend Modular operator/(const Modular& lhs, const Modular& rhs) {
ValueType result = SupperType(1) * lhs.value() * rhs.inv().value() % mod_;
return Modular(result);
}
std::string to_string() const { return std::string(value_); }
private:
ValueType value_;
};
// using Mint = Modular<int, 1'000'000'007, int64_t>;
using Mint = Modular<int, 998'244'353, int64_t>;
class Binom {
private:
std::vector<Mint> f, g;
public:
Binom(int n) {
f.resize(n + 1);
g.resize(n + 1);
f[0] = Mint(1);
for (int i = 1; i <= n; ++i)
f[i] = f[i - 1] * Mint(i);
g[n] = f[n].inv();
for (int i = n - 1; i >= 0; --i)
g[i] = g[i + 1] * Mint(i + 1);
}
Mint operator()(int n, int m) {
if (n < 0 || m < 0 || m > n)
return Mint(0);
return f[n] * g[m] * g[n - m];
}
};
void SolveCase(int Case) {
int n, k;
std::cin >> n >> k;
std::vector<i64> a(n), b(n), c(n);
for (int i = 0; i < n; ++i)
std::cin >> c[i];
for (int i = 0; i < n; ++i) {
i64 s = 0, s2 = 0;
for (int j = 0; j < 6; ++j) {
i64 x;
std::cin >> x;
s += x;
s2 += x * x;
}
a[i] = s;
b[i] = 6 * s2 - s * s - 36 * c[i];
}
// let z = +\inf first, and then decrease z gradually.
std::vector<std::pair<i64, i64>> p(n);
for (int i = 0; i < n; ++i)
p[i] = {a[i], b[i]};
std::sort(p.begin(), p.end(), std::greater<>());
std::vector<int> selected(n, false);
i64 X = 0, Y = 0;
for (int i = 0; i < k; ++i) {
X += p[i].first;
Y += p[i].second;
selected[i] = true;
}
i64 ans = X * X + Y;
// decrease z gradually.
std::vector<std::array<i64, 4>> swaps;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
auto [xi, yi] = p[i];
auto [xj, yj] = p[j];
// pi can replace pj only if xi < xj and yi > yj since we are maximizing cx + y.
if (xi < xj && yi > yj) {
swaps.push_back({xi - xj, yi - yj, i, j});
}
}
}
std::sort(swaps.begin(), swaps.end(),
[&](const std::array<i64, 4>& lhs, const std::array<i64, 4>& rhs) -> bool {
auto [dx1, dy1, _1, _2] = lhs;
auto [dx2, dy2, _3, _4] = rhs;
return dx1 * dy2 > dx2 * dy1;
});
for (auto [_1, _2, i, j] : swaps) {
if (!selected[i] && selected[j]) {
X += p[i].first - p[j].first;
Y += p[i].second - p[j].second;
ans = std::max(ans, X * X + Y);
selected[i] = true;
selected[j] = false;
}
}
std::cout << (Mint(ans) / Mint(36)).value() << "\n";
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】