转载自:https://lornd.top/index.php/archives/37/
比赛地址:AtCoder Beginner Contest 137
A - +-x
题目大意
给定两个数 \(A\) 、\(B\) 吗,求 \(A + B\) 、\(A - B\) 、\(A \times B\) 中的最大值
解题思路
#include <cstdio>
#include <algorithm>
int a, b;
int main() {
scanf("%d%d", &a, &b);
printf("%d", std::max(a + b, std::max(a - b, a * b)));
return 0;
}
B - One Clue
题目大意
有 \(2000001\) 块石头排成一列,编号从 \(-1000000\) 到 \(1000000\) ,其中有连续 \(K\) 块石头被染成了黑色,其中坐标为 \(X\) 的石头是黑色,求哪些石头可能被染成了黑色。
解题思路
分情况讨论,坐标为 \(X\) 的石头可能在这 \(K\) 个石头的任何位置,并且除了最左端或者最右端以外,其余位置所产生的答案都包含在了最左端或者最右端产生的答案之中,所以只需要计算出在这两种情况下,哪几块石头被染成了黑色即可。
#include <cstdio>
int k, x;
int main() {
scanf("%d%d", &k, &x);
for (int i = x - k + 1; i <= x + k - 1; ++i) {
printf("%d ", i);
}
return 0;
}
C - Green Bin
题目大意
定义一个字符串 \(a\) 为另一个字符串 \(b\) 的字谜,当且仅当 \(a\) 可以由 \(b\) 经重新排列组成,给定 \(n\) 个字符串,问有多少对 \((i,j)(i < j)\) 满足第 \(i\) 个字符串与第 \(j\) 个字符串互为字谜。
解题思路
根据题意我们可以发现,两个字符串互为字谜,当且仅当其每个字母排序之后得到的新的两个字符串是相等的,例如:greenbin
和 beginner
互为字谜,因为它们排序之后均得到了 beeginnr
这个字符串。
所以我们可以对这 \(n\) 个字符串分别进行排序,再总体对这 \(n\) 个字符串进行排序,以快速得到相同的字符串,然后统计每一种相同的字符串的个数,从中任取两个都能找到合适的 \((i,j)\) ,这就是一个组合数问题了。
#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
const int MAXN = 1e5 + 5;
int n;
int tmp[MAXN], cnt;
long long int ans;
std::string s[MAXN];
int main() {
std::cin >> n;
for (int i = 1; i <= n; ++i) {
std::cin >> s[i];
std::sort(s[i].begin(), s[i].end());
}
std::sort(s + 1, s + n + 1);
for (int i = 1; i <= n; ++i) {
if (s[i] != s[i - 1])
++cnt;
++tmp[cnt];
}
for (int i = 1; i <= cnt; ++i) {
ans += 1ll * tmp[i] * (tmp[i] - 1) >> 1;
}
std::cout << ans;
return 0;
}
D - Summer Vacation
题目大意
有 \(n\) 种工作,第 \(i\) 种工作需要一天的时间完成,完成后会在 \(A_i\) 天后(包括工作的这一天)获得 \(B_i\) 的收益,一共有 \(m\) 天时间,求最大收益。
解题思路
我们不妨倒着来贪心,剩下 \(1\) 天时,剩下 \(2\) 天时……每次,我们可以记录下剩下 \(i\) 天时能获得收益的工作,然后取收益最大的工作进行,而这可以直接使用优先队列进行完成。
#include <cstdio>
#include <queue>
#include <algorithm>
const int MAXN = 1e5 + 1;
int n, m, ans, cur;
struct job {
int wait, val;
job(int ww = 0, int vv = 0) {
wait = ww;
val = vv;
}
bool operator < (const job &another) const {
return wait < another.wait;
}
}arr[MAXN];
struct node {
int key;
node (int kk = 0) {
key = kk;
}
bool operator < (const node &another) const {
return key < another.key;
}
};
std::priority_queue<node> q;
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d%d", &arr[i].wait, &arr[i].val);
}
std::sort(arr + 1, arr + n + 1);
cur = 1;
for (int i = 1; i <= m; ++i) {
while (cur <= n && arr[cur].wait <= i) {
q.push(node(arr[cur].val));
++cur;
}
if (!q.empty()) {
ans += q.top().key;
q.pop();
}
}
printf("%d", ans);
return 0;
}
E - Coins Respawn
题目大意
有一个有 \(n\) 个点, \(m\) 条边的有向图,第 \(i\) 条边上有 \(c_i\) 个金币,可以重复收集。要从 \(1\) 走到 \(n\) ,并在结束(在 \(n\) 时可以选择继续,也可以选择结束)的时候支付 \(T\times P\) 个金币,其中 \(T\) 为走过的边数。求最多剩余多少金币,如果不存在最多,则输出 \(-1\) 。
解题思路
我们将每个 \(c_i\) 都减去 \(P\) 得到 \(d_i\) ,题目就变成了,每经过一条边搜集 \(d_i\) 个金币,问结束时最多剩余多少个金币,即 \(1\) 到 \(n\) 的最长路。
由于这个图有向,并且可能有环,所以不能采用拓扑排序 + DP 的方式,但是我们可以将 SPFA 的松弛操作中的小于号改成大于号,来求得最长路,同时判断正环(误解)的方法就和判负环一样,一个点的入队次数高于其入度(或者 \(n\) )即可。
但是,有正环并不代表无解,因为有可能这个通过正环并不能到达点 \(n\) ,因此我们还应该预处理出能够到达点 \(n\) 的点的集合,然后在 SPFA 的时候只走向能够到达点 \(n\) 的点,而这个预处理可以通过反向建图,求出从 \(n\) 点出发能够到达的点,就是我们要求的点了。
#include <cstdio>
#include <vector>
#include <queue>
#include <algorithm>
const int MAXN = 1e5 + 5;
int n, m, p, u, v, k;
int dist[MAXN], num[MAXN];
bool flag[MAXN], inQueue[MAXN];
struct graph {
int vertex, val;
graph(int vv = 0, int vval = 0) {
vertex = vv;
val = vval;
}
};
std::vector<graph> con[MAXN], reverse[MAXN];
std::queue<int> q;
void ins(int start, int end, int key, std::vector<graph> a[]) {
a[start].push_back(graph(end, key));
}
void init(int key) {
flag[key] = true;
for (int i = 0; i < reverse[key].size(); ++i) {
int nxt = reverse[key][i].vertex;
if (flag[nxt] == false)
init(nxt);
}
}
int spfa() {
for (int i = 2; i <= n; ++i) {
dist[i] = -(1e9 + 7);
}
dist[1] = 0;
q.push(1);
inQueue[1] = true;
while (!q.empty()) {
int cur = q.front();
q.pop();
inQueue[cur] = false;
for (int i = 0; i < con[cur].size(); ++i) {
int nxt = con[cur][i].vertex, cost = con[cur][i].val;
if (flag[nxt] && dist[nxt] < dist[cur] + cost) {
dist[nxt] = dist[cur] + cost;
if (!inQueue[nxt]) {
q.push(nxt);
inQueue[nxt] = true;
++num[nxt];
if (num[nxt] > n)
return -1;
}
}
}
}
return std::max(dist[n], 0);
}
int main() {
scanf("%d%d%d", &n, &m, &p);
for (int i = 1; i <= m; ++i) {
scanf("%d%d%d", &u, &v, &k);
ins(u, v, k - p, con);
ins(v, u, 1, reverse);
}
init(n);
printf("%d", spfa());
return 0;
}