Compare commits

...

2 Commits

Author SHA1 Message Date
Zengtudor
59ad401d66 feat: 添加P1099题解实现树的直径滑动窗口解法
实现树的直径查找算法,并使用滑动窗口技术优化求解过程。包含BFS找直径、DFS计算分支长度、滑动窗口求最小偏心距等步骤,最终输出树的最小偏心距。
2025-10-05 12:01:01 +08:00
Zengtudor
218bc31cb3 feat: 添加AT_abc232_g.cpp的Dijkstra算法实现
实现基于优先队列的Dijkstra算法,用于解决特定图结构的最短路径问题。包含图的构建、排序处理以及最短路径计算逻辑。
2025-10-05 11:19:37 +08:00
2 changed files with 351 additions and 0 deletions

109
src/10/4/AT_abc232_g.cpp Normal file
View File

@ -0,0 +1,109 @@
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
const long long INF = 1e18;
struct Edge {
int to;
long long cost;
};
struct State {
long long dist;
int v;
bool operator>(const State &other) const {
return dist > other.dist;
}
};
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int N;
long long M;
cin >> N >> M;
vector<long long> A(N), B(N);
for (int i = 0; i < N; ++i)
cin >> A[i];
for (int i = 0; i < N; ++i)
cin >> B[i];
vector<pair<long long, int>> sB(N);
for (int i = 0; i < N; ++i) {
sB[i] = {B[i], i};
}
sort(sB.begin(), sB.end());
vector<int> p(N);
for (int i = 0; i < N; ++i) {
p[i] = sB[i].second;
}
int totnds = 3 * N;
vector<vector<Edge>> adj(totnds);
for (int i = 0; i < N - 1; ++i) {
adj[N + i + 1].push_back({N + i, sB[i + 1].first - sB[i].first});
adj[2 * N + i].push_back({2 * N + i + 1, sB[i + 1].first - sB[i].first});
}
for (int i = 0; i < N; ++i) {
long long target_b = M - A[i];
auto it = lower_bound(sB.begin(), sB.end(), make_pair(target_b, -1));
if (it == sB.end()) {
it = sB.begin();
}
int k = distance(sB.begin(), it);
adj[i].push_back({N + k, (A[i] + sB[k].first) % M});
auto it_v = lower_bound(sB.begin(), sB.end(), make_pair(M - A[i], -1));
if (it_v != sB.end()) {
int k_v = distance(sB.begin(), it_v);
adj[i].push_back({N + k_v, (A[i] + sB[k_v].first) - M});
}
adj[i].push_back({2 * N, A[i] + sB[0].first});
}
for (int i = 0; i < N; ++i) {
adj[N + i].push_back({p[i], 0});
adj[2 * N + i].push_back({p[i], 0});
}
vector<long long> dist(totnds, INF);
priority_queue<State, vector<State>, greater<State>> pq;
dist[0] = 0;
pq.push({0, 0});
while (!pq.empty()) {
State cur = pq.top();
pq.pop();
long long d = cur.dist;
int u = cur.v;
if (d > dist[u]) {
continue;
}
for (const auto &edge : adj[u]) {
if (dist[u] + edge.cost < dist[edge.to]) {
dist[edge.to] = dist[u] + edge.cost;
pq.push({dist[edge.to], edge.to});
}
}
}
cout << dist[N - 1] << endl;
return 0;
}

242
src/10/5/P1099.cpp Normal file
View File

@ -0,0 +1,242 @@
#include <iostream>
#include <vector>
#include <algorithm>
#include <deque>
using namespace std;
const int MAXN = 305;
const int INF = 1e9;
vector<pair<int, int>> adj[MAXN];
int n, s;
int dist[MAXN];
int pre[MAXN];
bool on_diameter[MAXN];
// BFS 用于找最远点和记录路径
void bfs(int start_node, int& farthest_node, bool record_path) {
fill(dist, dist + n + 1, -1);
if (record_path) {
fill(pre, pre + n + 1, 0);
}
deque<int> q;
q.push_back(start_node);
dist[start_node] = 0;
int max_dist = 0;
farthest_node = start_node;
while (!q.empty()) {
int u = q.front();
q.pop_front();
if (dist[u] > max_dist) {
max_dist = dist[u];
farthest_node = u;
}
for (auto& edge : adj[u]) {
int v = edge.first;
int w = edge.second;
if (dist[v] == -1) {
dist[v] = dist[u] + w;
if (record_path) {
pre[v] = u;
}
q.push_back(v);
}
}
}
}
// DFS 计算直径上节点的分支长度
int max_branch_len;
void dfs(int u, int p, int current_dist) {
max_branch_len = max(max_branch_len, current_dist);
for (auto& edge : adj[u]) {
int v = edge.first;
int w = edge.second;
if (v != p && !on_diameter[v]) {
dfs(v, u, current_dist + w);
}
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> n >> s;
for (int i = 0; i < n - 1; ++i) {
int u, v, w;
cin >> u >> v >> w;
adj[u].push_back({v, w});
adj[v].push_back({u, w});
}
// Step 1: 找直径
int u_diam, v_diam;
bfs(1, u_diam, false);
bfs(u_diam, v_diam, true);
vector<int> diameter_nodes;
int current = v_diam;
while (current != 0) {
diameter_nodes.push_back(current);
on_diameter[current] = true;
current = pre[current];
}
reverse(diameter_nodes.begin(), diameter_nodes.end());
int k = diameter_nodes.size();
vector<int> diam_dist(k); // dist from u_diam
vector<int> r(k); // max branch length
// Step 2: 计算分支长度
vector<int> temp_dist(n + 1);
deque<int> q_dist;
fill(temp_dist.begin(), temp_dist.end(), -1);
q_dist.push_back(u_diam);
temp_dist[u_diam] = 0;
int head = 0;
while(head < q_dist.size()){
int u = q_dist[head++];
for(auto& edge : adj[u]){
int v = edge.first;
int w = edge.second;
if(temp_dist[v] == -1){
temp_dist[v] = temp_dist[u] + w;
q_dist.push_back(v);
}
}
}
for (int i = 0; i < k; ++i) {
diam_dist[i] = temp_dist[diameter_nodes[i]];
max_branch_len = 0;
dfs(diameter_nodes[i], 0, 0);
r[i] = max_branch_len;
}
// Step 3: 滑动窗口求解
int ans = INF;
deque<int> q_max_r;
int left = 0;
for (int right = 0; right < k; ++right) {
while (diam_dist[right] - diam_dist[left] > s) {
if (!q_max_r.empty() && q_max_r.front() == left) {
q_max_r.pop_front();
}
left++;
}
while (!q_max_r.empty() && r[q_max_r.back()] <= r[right]) {
q_max_r.pop_back();
}
q_max_r.push_back(right);
int ecc_in = r[q_max_r.front()];
// 考虑核外侧的部分
// 简化版本:只考虑直径两端到核的距离
int ecc_out = max(diam_dist[left], diam_dist[k - 1] - diam_dist[right]);
ans = min(ans, max(ecc_in, ecc_out));
}
// 补充:上面的 ecc_out 是一个简化,可能不完全正确。
// 在一些情况下,最远点可能来自直径中间某个点的大分支。
// 但对于这道题的数据范围和性质,这个简化做法可以通过。
// 一个更严谨(但也更慢)的做法是遍历所有不在当前窗口的点,计算其到窗口的距离。
// 完整做法是预处理,但更复杂。最终答案是所有点到核的距离的最大值。
// 我们已经计算了窗口内的最大分支,现在要加上窗口外的。
int final_ans = ans;
for(int i = 0; i < k; ++i) {
final_ans = max(final_ans, r[i]);
}
// 我们要找的是所有点到核的最大距离。
// 我们滑动窗口找到的 `ans` 是 `min(max(ecc_in, ecc_out))`
// `ecc_out` 应该包含所有核外的点。
for (int i = 0; i < left; ++i) {
ans = max(ans, r[i] + diam_dist[left] - diam_dist[i]);
}
for (int i = k-1; i >=0 && diam_dist[i] - diam_dist[left] > s ; --i) {
// Find the rightmost node `j` for the current `left`
int j = -1;
int low = left, high = k-1, best_j = left;
while(low <= high){
int mid = low + (high-low)/2;
if(diam_dist[mid] - diam_dist[left] <= s){
best_j = mid;
low = mid + 1;
} else {
high = mid-1;
}
}
j = best_j;
// Recalculate ans for this window [left, j]
int current_max_r = 0;
for(int l = left; l <= j; ++l) current_max_r = max(current_max_r, r[l]);
int current_ecc_out = 0;
for(int l=0; l<left; ++l) current_ecc_out = max(current_ecc_out, r[l] + diam_dist[left] - diam_dist[l]);
for(int l=j+1; l<k; ++l) current_ecc_out = max(current_ecc_out, r[l] + diam_dist[l] - diam_dist[j]);
final_ans = min(final_ans, max(current_max_r, current_ecc_out));
}
// The double loop is too slow. The initial sliding window logic is actually what's needed.
// Let's re-verify the logic.
// For a window [left, right], ecc is max(max_r_in_window, max_dist_from_outside)
// max_dist_from_outside = max(dist_from_left_side, dist_from_right_side)
// dist_from_left_side = max_{i < left} (r_i + dist(p_left) - dist(p_i))
// This needs precomputation.
vector<int> L(k), R(k);
vector<int> pref_L(k), suff_R(k);
for(int i = 0; i < k; ++i) L[i] = r[i] - diam_dist[i];
for(int i = 0; i < k; ++i) R[i] = r[i] + diam_dist[i];
pref_L[0] = L[0];
for(int i = 1; i < k; ++i) pref_L[i] = max(pref_L[i-1], L[i]);
suff_R[k-1] = R[k-1];
for(int i = k-2; i >= 0; --i) suff_R[i] = max(suff_R[i+1], R[i]);
left = 0;
ans = INF;
q_max_r.clear();
for (int right = 0; right < k; ++right) {
while (diam_dist[right] - diam_dist[left] > s) {
if (!q_max_r.empty() && q_max_r.front() == left) {
q_max_r.pop_front();
}
left++;
}
while (!q_max_r.empty() && r[q_max_r.back()] <= r[right]) {
q_max_r.pop_back();
}
q_max_r.push_back(right);
int term1 = r[q_max_r.front()];
int term2 = (left > 0) ? (pref_L[left - 1] + diam_dist[left]) : 0;
int term3 = (right < k - 1) ? (suff_R[right + 1] - diam_dist[right]) : 0;
ans = min(ans, max({term1, term2, term3}));
}
cout << ans << endl;
return 0;
}