AtCoder Beginner Contest 061
A
给三个整数 \(A, B, C\) ,确定是否有 \(C \geq A, C \leq B\) 。即 \(A \leq C \leq B\) 。
int A, B, C; std::cin >> A >> B >> C;
std::cout << ((A <= C && C <= B) ? "Yes" : "No") << "\n";
B
题意:
给 \(N\) 个城市 \(M\) 条路,第 \(i(1 \leq i \leq M)\) 条路双向地连接城市 \(a_i\) 和 \(b_i\) (\(1 \leq a_i, b_i \leq N\))。
询问每个城市有多少条公路。
\(1 \leq N, M \leq 50, 1\leq a_i, b_i \leq N, a_i \neq b_i\)
题解:
即询问每个点的度。
int N, M; std::cin >> N >> M;
std::vector<int> d(N + 1);
for (int i = 1; i <= M; i++) {
int u, v; std::cin >> u >> v;
d[u]++; d[v]++;
}
for (int i = 1; i <= N; i++) std::cout << d[i] << "\n";
时间复杂度 \(O(N + M)\) 。
C
题意:
有一个空集合。有 \(N\) 次操作将会插入元素到空数组中。第 \(i\) 次操作,插入 \(b_i\) 个 \(a_i\) 到原始集合中。
询问插入操作结束后,第 \(K\) 小的数。
\(1 \leq N, a_i, b_i \leq 10^{5}, 1 \leq K \leq \sum_{i = 1}^{N} b_i\)
题解:
首先不需要离散化(没有很大意义)。
考虑直接维护一个权值数组,求出前缀和。类似一维数点,二分第 \(K\) 小。
const int MAXN = 1E5+10;
ll a[MAXN];
void solve() {
int N; ll K;
std::cin >> N >> K;
int mx = 0;
for (int i = 1; i <= N; i++) {
int x, y; std::cin >> x >> y;
a[x] += y;
mx = std::max(mx, x);
}
for (int i = 1; i <= mx; i++) a[i] += a[i - 1];
int L = 0, R = mx + 1;
while ( R - L > 1 ) {
int M = L + R >> 1;
if (a[M] >= K) R = M;
else L = M;
}
std::cout << R << "\n";
}
时间复杂度 \(O(N + \log N)\) 。
给题的方式很奇怪,原 idea 似乎是个动态询问。
实际上就是动态全局第 K 小问题。在 BIT 上倍增查找做到 \(O(N \log N)\) 。
再难一点会套一个离散化的实现。
D
题意:
一个图,有 \(N\) 个点 \(M\) 条边,第 \(i\) 条边从 \(a_i\) 连向 \(b_i\) ,权值为 \(c_i\) 。
棋子一开始在 \(1\) 号点,玩家可以让棋子沿着有向边走,且可以在 \(N\) 号点选择结束。保证点 \(1\) 到点 \(N\) 存在通路。
一个棋子可以从 \(u\) 走到 \(v\) ,当且仅当 \(u\) 向 \(v\) 连接了一条边。并且可以获得 \(c_{e(u, v)}\) 的积分。
询问游戏结束时玩家可以得到的最大积分。若可以得到无限积分则输出 \(inf\) 。
\(2 \leq N \leq 100, 1 \leq M \leq min(\frac{N(N - 1)}{2}, 2000)\)
\(1 \leq a_i, b_i \leq N(1 \leq i \leq M), a_i \neq b_i, a_i \neq a_j, b_i \neq b_j\) 即没有自环和重边
\(-10^{9} \leq c_i \leq 10^{9}\)
题解:
若存在正环,则 \(s\) 一定在该正环内。只要保证这个正环于 \(s\) 到 \(t\) 的路径和某个正环有交 。则可以得到无限积分。
只需要让包含 \(s\) 的负环可达 \(t\) 即可。
否则找 \(1\) 到 \(N\) 的最长路。
1. 找正环
首先环是好找的。最小环也是好找的。
如何找负环?使用 Bellman-ford 算法(SPFA 是卡常,且一定卡不过)。
如何找正环?在反权图上找负环。
Bellman-ford 实际上是反复迭代的暴力。每条边最多松弛 \(N - 1\) 次,于是 Beelman-ford 最坏有 \(N - 1\) 轮迭代。可以通过 \(O(N M)\) 的时间复杂度求出非正权图的单源最短路。
找负环:
对于一个点 \(s\) ,若从 \(s\) 开始的 Bellman-ford 迭代轮数 \(\geq N\) ,则图上存在负环 \(G\) 满足 \(s \in G\) 。
并且可以再跑一轮 Bellman-ford 找到 \(G\) 。
若 \(e\) 会且能被松弛,一定满足 \(e_u, e_v \in G\) 。
容易证明每一轮迭代,负环上的边一定会松弛。 负环被确定后。每个点到源点的距离一定小于上一轮的距离。否则这个点不在负环上。
有向图上,环可达的点:
在得到负环的情况下,还要找负环可达的点。
若一条边 \(e\) 满足 \(e_u \in G\) 或者 \(e_u\) 被负环可达,则 \(e_v\) 被负环可达。
每次迭代最坏扩展一个可达节点,于是最坏需要需要 \(N - 1\) 轮迭代。
找正环的时间复杂度为 \(O(N M)\) 。
2. 单源最长路
如果不是 DAG 。反权图上的单源最短路即原图上的单元最短路。显然是 Bellman-ford时间复杂度 \(O(N M)\) 。
int N, M; std::cin >> N >> M;
std::vector<std::array<int, 3> > E(M + 1);
for (int i = 1; i <= M; i++) {
int u, v, c; std::cin >> u >> v >> c;
E[i] = {u, v, c};
}
std::vector<std::array<int, 3> > anti_E(E.begin(), E.end());
for (int i = 1; i <= M; i++) anti_E[i][2] *= -1;
std::vector<ll> dist(N + 1, llinf);
dist[1] = 0;
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= M; j++) {
int u = anti_E[j][0], v = anti_E[j][1], c = anti_E[j][2];
if (dist[u] == llinf) continue;
if (dist[u] + c < dist[v]) {
dist[v] = dist[u] + c;
}
}
}
std::vector<int> negative(N + 1);
std::vector<int> can(N + 1);
for (int j = 1; j <= M; j++) {
int u = anti_E[j][0], v = anti_E[j][1], c = anti_E[j][2];
if (dist[u] == llinf) continue;
if (dist[u] + c < dist[v])
negative[v] = can[v] = 1;
}
for (int i = 1; i <= N - 1; i++) {
for (int j = 1; j <= M; j++) {
int u = anti_E[j][0], v = anti_E[j][1], c = anti_E[j][2];
if (dist[u] == llinf) continue;
if (can[u])
can[v] = 1;
}
}
if (can[N]) std::cout << "inf\n";
else std::cout << -dist[N] << "\n";
浙公网安备 33010602011771号