AtCoder Beginner Contest 175 (AB水,C数学,D思维+前缀和处理+进价思考,E方阵+条件DP,F新回文字符串处理 GJ)
补题链接:Here
A - Rainy Season
如果不是
RSR
型的话直接计算R
的数量即可
B - Making Triangle
给定 \(N\) 根长度分别为 \(L_i\) 的棍子,问能组成多少个三边长度各不相同的三角形?如果两个三角形至少用了一根不同编号的棍子,则称它们是不同的三角形。
由于数据范文较小 (\(N \le 100\)),所以我们可以排序以后枚举三元组即可。
另外 CP wiki 提到这里进一步优化的话,可以在固定最长边的基础上,用双指针确定另外两条边的长度范围,这样时间复杂度就降到的了 \(\mathcal{O}(N^2)\)。
C - Walking Takahashi
题意:有一个数 \(X\) ,对它进行 \(K\) 次 \(+D\) 或 \(−D\) 的操作,求操作后的 \(\min|X'|\)。
思路:
首先XX的正负不影响结果,所以我们可以只考虑 \(|X|\)。
如果 \(|X|>D\),那么我们首先应该向原点移动,直到 \(|X'|<D\)。这时还剩下 \(K′\) 次操作,我们应当在原点的左右两侧来回移动。根据 \(K′\) 的奇偶判断一下最后在哪一个位置即可。
using ll = long long;
int main() {
ios_base::sync_with_stdio(false), cin.tie(0);
ll X, K, D, R;
cin >> X >> K >> D;
if (X < 0) X = -X;
R = X / D;
if (K < R) {
cout << (X - K * D);
return 0;
}
K -= R, X -= R * D;
cout << (K & 1 ? D - X : X);
return 0;
}
D - Moving Piece
有 \(N(N\leq5000)\)个方格,从第 \(i\) 个方格会跳到第 \(P_i\) 个方格。PP是 \(1,\cdots,N\)的一个排列。
每个方格上写了一个数字 \(C_i\) 。每次跳跃时,会得到等同于 \(C_{P_i}\) 的分数。你可以从任意方格开始,跳跃至少一次,至多 \(K\) 次,求能够取得的最高分数。
思路:枚举起点。由于 \(P\) 是排列,所以我们从任意位置 \(i\) 开始,经过若干次跳跃后一定会回到 \(i\) 。我们可以计算出一个周期内的前缀和。然后,根据周期长度 \(C\) 与 \(K\) 之间的关系,分情况讨论。
\(K\leq C\),此时我们应该选择前 \(K\) 个前缀和中的最大值。
\(K>C\),令\(K=nc+r\),则我们可以选择
- 不循环,选择所有前缀和中的最大值。
- 循环 \(n\) 次,再加上前 \(r\) 个前缀和中的最大值。
- 循环 \(n−1\) 次,再加上所有前缀和中的最大值。
\(\mathcal{O}(N^2)\)
// Murabito-B 21/04/08
#include <bits/stdc++.h>
using ll = long long;
using namespace std;
int main() {
ios_base::sync_with_stdio(false), cin.tie(0);
int n, k;
cin >> n >> k;
vector<int> p(n), c(n);
for (int i = 0; i < n; ++i) cin >> p[i];
for (int i = 0; i < n; ++i) cin >> c[i];
ll ans = LLONG_MIN; // long long的最小值
for (int i = 0; i < n; ++i) {
vector<bool> book(n);
int idx = i;
vector<ll> sum = {0}, hi = {LLONG_MIN};
while (!book[p[idx] - 1]) {
idx = p[idx] - 1;
book[idx] = true;
sum.emplace_back(sum.back() + c[idx]);
hi.emplace_back(max(hi.back(), sum.back()));
}
int m = sum.size() - 1;
int f = k / m, res = k % m;
ll result = 0;
if (f > 0) result = max(hi[m], max(sum[m] * f + (res == 0 ? 0 : hi[res]), sum[m] * (f - 1) + hi[m]));
else
result = hi[res];
ans = max(ans, result);
}
cout << ans << "\n";
return 0;
}
另外如果 \(N \le 10^5\) 呢?应该如何改进算法?
这里想了很久,只想到了 RMQ解决但代码部分没写出来,只能转载一下 CP wiki 的了
提示一:
在上面的算法中,对于一个循环,设其长度为 \(L\) ,我们实际上重复计算了 \(L\) 次(针对每一个起点)。有没有可能减少这样的重复计算呢?
提示二
在每一个循环内,问题实际上可以转化为,给定一个由 \(L\) 个数围成的圈,从中取出长度不超过\(K\)的一段连续串,求能取得的最大和。
提示三
前缀和+RMQ。
// Murabito-B 21/04/08
#include <bits/stdc++.h>
using ll = long long;
#define MAXN 5005
#define K 15
using namespace std;
const ll LO = -1e16;
int n, k;
ll st[MAXN * 2][K];
ll query(int l, int r) {
int len = r - l + 1;
int j = log2(len);
return min(st[l][j], st[r - (1 << j) + 1][j]);
}
ll solve(vector<int> &v) {
int len = v.size();
vector<ll> s = {0};
for (int i = 0; i < 2 * len; ++i)
s.emplace_back(s.back() + v[i % len]);
int slen = s.size();
for (int i = 0; i < slen; ++i)
st[i][0] = s[i];
for (int j = 1; j <= log2(slen); ++j)
for (int i = 0; i < slen; ++i) {
st[i][j] = st[i][j - 1];
int right = i + (1 << (j - 1));
if (right < slen)
st[i][j] = min(st[i][j], st[right][j - 1]);
}
ll sum = s[len], hi_r = LO, hi_all = LO;
int r = k % len;
for (int i = 1; i < slen; ++i) {
if (r)
hi_r = max(hi_r, s[i] - query(max(0, i - r), i - 1));
hi_all = max(hi_all, s[i] - query(max(0, i - len), i - 1));
}
if (k < len)
return hi_r;
return max(hi_all, max(sum * (k / len - 1) + hi_all, sum * (k / len) + hi_r));
}
int main() {
cin >> n >> k;
vector<int> p(n), c(n);
for (int i = 0; i < n; ++i)
cin >> p[i];
for (int i = 0; i < n; ++i)
cin >> c[i];
ll ans = LO;
vector<bool> vis(n);
for (int i = 0; i < n; ++i) {
if (vis[i])
continue;
vector<int> v;
int idx = i;
while (!vis[p[idx] - 1]) {
idx = p[idx] - 1;
vis[idx] = true;
v.emplace_back(c[idx]);
}
ans = max(ans, solve(v));
}
cout << ans;
}
E - Picking Goods
\(R\) 行 \(C\) 列的方阵,其中有 \(K\) 个格子里有东西,第ii个东西的价值为 \(v_i\)。从左上角走到右下角,只能向下或向右走,限定每行最多拿 $ 3$ 个东西,求能取得的最大价值。
简单的方阵 DP 再加一维记录当前行取了几个东西即可。因为\(3\) 是常数,所以总时间复杂度为:\(\mathcal{O}(RC)\)。
// Murabito-B 21/04/08
#include <bits/stdc++.h>
using ll = long long;
using namespace std;
ll dp[3010][3010][4] = {0};
int main() {
ios_base::sync_with_stdio(false), cin.tie(0);
int R, C, K;
cin >> R >> C >> K;
vector<vector<int>> a(R + 1, vector<int>(C + 1));
for (int i = 0; i < K; ++i) {
int r, c, v;
cin >> r >> c >> v;
a[r][c] = v;
}
for (int i = 1; i <= R; ++i)
for (int j = 1; j <= C; ++j) {
for (int k = 0; k <= 3; ++k)
dp[i][j][0] = max(dp[i][j][0], dp[i - 1][j][k]);
for (int k = 0; k <= 3; ++k)
dp[i][j][k] = max(dp[i][j][k], dp[i][j - 1][k]);
if (a[i][j])
for (int k = 3; k > 0; --k)
dp[i][j][k] = max(dp[i][j][k], dp[i][j][k - 1] + a[i][j]);
}
ll ans = 0;
for (int i = 0; i <= 3; ++i) ans = max(ans, dp[R][C][i]);
cout << ans << "\n";
return 0;
}
F - Making Palindrome
F 题是懵逼ing
有\(N\)(\(N\leq50\))个长度不超过 \(L\)(\(L\leq20\))的字符串,每个字符串可以使用无限次,第ii个字符串使用一次的代价为 \(C_i\)。问最少花费多少代价,能够用这些字符串组成一个回文串?或者说明无解。
大佬题解:
直接搜索,状态似乎是无穷无尽的。如何减少状态空间,让搜索变为可能?
我们考虑从左右两边分别构建字符串。最开始,左边和右边都是空的。我们希望最后能将左边部分和右边部分进行匹配。这里,匹配的意思是,对于串 \(A\) 和 \(B\),两串中较短的那串是较长那串的子串。在匹配之后,如果剩下的部分是一个回文串(或为空),则我们就成功构建了一个回文串。
我们每次可以把某个字符串加入到左边或右边,这样就得到一个中间状态。在转移过程中,我们应当保证始终只有至多一边有未匹配部分,而其余部分都应该得到匹配。也就是说,如果当前左边有未被匹配的部分,我们就把新字符串添加到右边;反之亦然。
从而,我们只需要保存当前未被匹配的部分。而因为我们总是在相反的一边添加,这里的未被匹配部分必定为原来某个字符串的前缀或后缀。这样,我们就把总状态数限制到了\(O(NL)\)。
此时,原题就变成了一个最短路径问题。因为数据范围很小,可以用各种最短路径算法来求解。
// Murabito-B 21/04/08
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define INF 10000000000000000LL
bool is_palindrome(string &s) {
int n = s.size();
for (int i = 0; i < n / 2; ++i)
if (s[i] != s[n - i - 1])
return false;
return true;
}
int n;
unordered_map<string, ll> memo[2];
unordered_set<string> vis[2];
vector<string> S[2];
vector<ll> C;
ll dfs(string s, int p) {
if (memo[p].count(s))
return memo[p][s];
if (is_palindrome(s))
return 0;
if (vis[p].count(s))
return INF;
vis[p].insert(s);
ll ans = INF;
int ls = s.size();
for (int i = 0; i < n; ++i) {
string t = S[!p][i];
int lt = t.size();
int l = min(ls, lt);
string ps = s.substr(0, l);
string pt = t.substr(0, l);
if (ps != pt)
continue;
ll cost =
ls > lt ? dfs(s.substr(l, ls - l), p) : dfs(t.substr(l, lt - l), !p);
if (cost < ans)
ans = min(ans, cost + C[i]);
}
vis[p].erase(s);
memo[p][s] = ans;
return ans;
}
int main() {
cin >> n;
S[0] = vector<string>(n);
S[1] = vector<string>(n);
C = vector<ll>(n);
ll ans = INF;
for (int i = 0; i < n; ++i) {
cin >> S[0][i] >> C[i];
S[1][i] = string(S[0][i].rbegin(), S[0][i].rend());
}
for (int i = 0; i < n; ++i)
ans = min(ans, dfs(S[0][i], 0) + C[i]);
cout << (ans == INF ? -1 : ans);
}
AtCoder Beginner Contest 175 (AB水,C数学,D思维+前缀和处理+进价思考,E方阵+条件DP,F新回文字符串处理 GJ)的更多相关文章
- AtCoder Beginner Contest 170 D - Not Divisible (数学)
题意:有一长度为\(n\)的数组,求该数组中有多少元素不能整除其它任一元素的个数. 题解:刚开始写了个分解质因数(我是傻逼),后来发现直接暴力枚举因子即可,注意某个元素出现多次时肯定不满足情况,再特判 ...
- AtCoder Beginner Contest 084 D - 2017-like Number【数论/素数/前缀和】
D - 2017-like Number Time limit : 2sec / Memory limit : 256MB Score : 400 points Problem Statement W ...
- AtCoder Beginner Contest 184 题解
AtCoder Beginner Contest 184 题解 目录 AtCoder Beginner Contest 184 题解 A - Determinant B - Quizzes C - S ...
- AtCoder Beginner Contest 052
没看到Beginner,然后就做啊做,发现A,B太简单了...然后想想做完算了..没想到C卡了一下,然后还是做出来了.D的话瞎想了一下,然后感觉也没问题.假装all kill.2333 AtCoder ...
- AtCoder Beginner Contest 068 ABCD题
A - ABCxxx Time limit : 2sec / Memory limit : 256MB Score : 100 points Problem Statement This contes ...
- AtCoder Beginner Contest 154 题解
人生第一场 AtCoder,纪念一下 话说年后的 AtCoder 比赛怎么这么少啊(大雾 AtCoder Beginner Contest 154 题解 A - Remaining Balls We ...
- AtCoder Beginner Contest 153 题解
目录 AtCoder Beginner Contest 153 题解 A - Serval vs Monster 题意 做法 程序 B - Common Raccoon vs Monster 题意 做 ...
- 题解 AtCoder Beginner Contest 168
小兔的话 欢迎大家在评论区留言哦~ AtCoder Beginner Contest 168 A - ∴ (Therefore) B - ... (Triple Dots) C - : (Colon) ...
- AtCoder Beginner Contest 223
AtCoder Beginner Contest 223 A是纯纯的水题,就不说了 B - String Shifting 思路分析 我真的sb,一开始想了好久是不是和全排列有关,然后读了好几遍题目也 ...
- AtCoder Beginner Contest 148 题解
目录 AtCoder Beginner Contest 148 题解 前言 A - Round One 题意 做法 程序 B - Strings with the Same Length 题意 做法 ...
随机推荐
- Kafka 如何保证消息消费的全局顺序性
哈喽大家好,我是咸鱼 今天我们继续来讲一讲 Kafka 当有消息被生产出来的时候,如果没有指定分区或者指定 key ,那么消费会按照[轮询]的方式均匀地分配到所有可用分区中,但不一定按照分区顺序来分配 ...
- [NewStarCTF WEEK5] pwn-planet 详解
这道题目更多是考pwner的逆向功底(虽然程序逻辑也不是非常复杂=_=) 老规矩,先checksec查看程序 保护全开 看一下main函数 __int64 __fastcall main(int a1 ...
- TIOBE 12月榜单: C# 即将成为2023 年度编程语言
TIOBE 公布了 2023 年 12 月的编程语言排行榜. 2022年C# 在挑战成为年度编程语言,但在最后一刻,C++出人意料地夺得了冠军.今年,我们确信 C# 将获胜成为2023年度编程语言.它 ...
- 用pycharm创建一个django框架
用pycharm创建一个django框架 注意解释器的选择和文件路径 创建完django项目 1.自动创建了一个templates目录(先删除) 2.把settings里的 TEMPLATES = [ ...
- [GDOIpj222A] 点指兵兵
第一题 点指兵兵 提交文件: bing.cpp 输入文件: bing.in 输出文件: bing.out 时间空间限制: 1 秒, 256 MB 你一定有过在两个物品之间犹豫不决的时候,想要借助一些方 ...
- Go 语言区块链测试:实践指南
引言 Go 语言在区块链开发中的应用日益增多,凭借其简洁的语法和强大的并发支持,成为开发区块链应用的热门选择.理解和实践 Go 语言的单元测试对于保证区块链应用的质量和稳定性至关重要. Go 单元测试 ...
- 中企网安信息科技:基于数据化大屏的BI数据分析管理系统概述
由华企网安总公司北京中企网安信息科技有限责任公司开发的<基于数据化大屏的BI数据分析管理系统>,获得国家版权局颁发的计算机软件著作权登记证书. 基于数据化大屏的BI数据分析管理系统利用大数 ...
- MinIO客户端之du
MinIO提供了一个命令行程序mc用于协助用户完成日常的维护.管理类工作. 官方资料 mc du 用于输出桶内对象的数量和占用的空间. 命令如下: ./mc du local1/bkt1 控制台的输出 ...
- ElasticSearch之Analyze index disk usage API
本API用于分析.统计指定index当前占用的存储空间. 考虑到本特性目前仍然处于预览状态,因此使用方法.参数等可能会发生变化,或者未来也许会被删除. 本API暂时不建议在生产系统中使用. 命令样例如 ...
- Ubuntu安装Maridb 10.5版本
以20.04版本为例 Ubutun20.04自带源默认安装的mariadb版本为10.3不符合安装zabbix6.0的要求 打开MariaDB的官方网站:https://mariadb.org/mar ...