1223 字
6 分钟
P2680 [NOIP2015 提高组] 运输计划
题目
公元 年,人类进入了宇宙纪元。
L 国有 个星球,还有 条双向航道,每条航道建立在两个星球之间,这 条航道连通了 L 国的所有星球。
小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 号星球沿最快的宇航路径飞行到 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 ,任意飞船驶过它所花费的时间为 ,并且任意两艘飞船之间不会产生任何干扰。
为了鼓励科技创新, L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小 P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。
在虫洞的建设完成前小 P 的物流公司就预接了 个运输计划。在虫洞建设完成后,这 个运输计划会同时开始,所有飞船一起出发。当这 个运输计划都完成时,小 P 的物流公司的阶段性工作就完成了。
如果小 P 可以自由选择将哪一条航道改造成虫洞, 试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少? 对于 的数据,保证:,,。
题解
首先提炼出题目要求——最大值最小 想到二分最大值 想一想二分之后如何判断是否合法 把大于 最大值 的路径都选出来,在树上标记出来 如果有一个边被选出来的所有路径都包括,且这其中最长的路径减去 边权小于 二分值,那么就合法,否则就不
实现问题
把大于 最大值 的路径都选出来
路径长度可以用树上前缀和
把大于 最大值 的路径都选出来,在树上标记出来
树上差分
出现的问题
1.二分写错 2.要用upper_bound 3.二分测试没全部初始化
AC代码
#include<bits/stdc++.h>using namespace std;const int N = 3e5 + 100;int head[N], nxt[N << 1], edge[N << 1], to[N << 1], cnt = 1;
int point_to_edge[N];
int n, m;
int longest_path;struct U{ int m, n, length;
bool operator < (const U o) const { return length < o.length; }} u[N];
// <lca>
int anc[N][21], depth[N];
void lca_init(){ for(int j = 1; j <= 20;j++) { for(int i = 1;i <= n;i++) { anc[i][j] = anc[anc[i][j - 1]][j - 1]; } }}
int lca_query(int x, int y){ if(depth[x] < depth[y]) swap(x, y);
for(int j = 20;j >= 0;j--) { if(depth[anc[x][j]] >= depth[y]) { x = anc[x][j]; } }
if(x == y) return x; for(int j = 20;j >= 0;j--) { if(anc[x][j] != anc[y][j]) { x = anc[x][j], y = anc[y][j]; } } return anc[x][0];}// </lca>
// <预处理前缀和+lca>
int sum[N];void dfs_sum(int u, int fa){ depth[u] = depth[fa] + 1; anc[u][0] = fa; for(int i = head[u]; i; i = nxt[i]) { int v = to[i]; if(v == fa) continue; point_to_edge[v] = i; sum[v] = sum[u] + edge[i]; dfs_sum(v, u); }}
// </预处理前缀和>
// <快读>int read(){ int f = 1, x = 0; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return f * x;}
// </快读>
void add(int x, int y, int z){ to[++cnt] = y; edge[cnt] = z; nxt[cnt] = head[x]; head[x] = cnt;}
// <check>
// <树上差分>
int differ[N], sum_differ[N];int best_can_edge_quan = 0;
void dfs_differ(int u, int fa, int T){ sum_differ[u] += differ[u]; for(int i = head[u]; i; i = nxt[i]) { int v = to[i]; if(v == fa) continue; dfs_differ(v, u, T); sum_differ[u] += sum_differ[v]; } if(sum_differ[u] == (m - T + 1) && best_can_edge_quan < edge[point_to_edge[u]]) { best_can_edge_quan = edge[point_to_edge[u]]; }
// cout << "qwq "<<u <<" "<< sum_differ[u] << endl;}
// </树上差分>
bool check(int T){ best_can_edge_quan = 0; memset(differ, 0, sizeof(differ)); memset(sum_differ, 0, sizeof(sum_differ));
U temp; temp.m = temp.n = 0; temp.length = T; int first_bad_order = upper_bound(u + 1, u + 1 + m/*!!!*/, temp) - u; if(temp.length >= u[first_bad_order].length && first_bad_order == m) return 1;
// cout << "qwq"<<first_bad_order << endl;// cout << first_bad_order << endl; for(int i = first_bad_order;i <= m;i++) { differ[u[i].m]++, differ[u[i].n]++, differ[lca_query(u[i].n, u[i].m)] -= 2; }
dfs_differ(1, 0, first_bad_order);
// cout << "qwq" << best_can_edge_quan << endl; if(longest_path - best_can_edge_quan <= T) { return 1; } return 0;}
// </check>
int binary_search(int r){ int l = 0;
while(l < r) { int mid = (l + r) >> 1;// cout << mid << endl; if(check(mid)/*可行*/) { r = mid;// cout << mid << " ok" << endl; } else { l = mid + 1;// cout << mid << " n" << endl; } } return l;}
int main(){ n = read(), m = read();
int x, y, z; for(int i = 1;i <= n - 1;i++) { x = read(), y = read(), z = read(); add(x, y, z), add(y, x, z); } dfs_sum(1, 0); lca_init();
for(int i = 1;i <= m;i++) { u[i].m = read(), u[i].n = read(), u[i].length = sum[u[i].n] + sum[u[i].m] - 2 * sum[lca_query(u[i].n, u[i].m)]; longest_path = max(longest_path, u[i].length); }
sort(u + 1, u + 1 + m);
printf("%d\n", binary_search(longest_path));
// differ[2] ++, differ[6] ++, differ[4] ++,differ[1] -= 2;//// dfs_differ(1, 0, 0);// cout << sum_differ[1];// cout << edge[point_to_edge[3]]; return 0;}
其实树上差分合树上前缀和可以优化,不用递归
P2680 [NOIP2015 提高组] 运输计划
https://blog.histcat.top/posts/p2680/