UOJ 150|NOIP 2015 Day 2|运输计划|LCA
2016-07-21 12:05
369 查看
Description
给出N点树和M条树上简单路径,求将一条边的边权改为0后使M条路径中最长的最短,求最小化的最长路径的长度。Method
题目描述来看容易联系到二分答案,当我们二分长度时,显然不需要考虑比二分的答案要短的路径,对于长的路径,我们只能修改一条边的长度,因此这条边必须在较长路径中都出现,即这些路径的路径交上的边。考虑序列上的区间交的求法,即在两端点打标记,那么将某个区间归属于其左端点,求前缀和后若某端点的前缀和=区间数量,那么其表示的区间是区间交的一部分。当然区间交是连续的。
在树上也可以这么做,在路径两端点和LCA处打标记,这里我们将某条边归属于深度较大的端点上。那么就从叶节点到根的方向求一次前缀和(倒BFS序),前缀和值=路径数量的点所表示的边就是路径交的一部分。
这个算法显然是O(n)的,遍历一遍路径交中的边,如果其边长足够所有较长路径都变小后小于二分答案就是满足条件的。当然,如果最长的路径满足了,那么其他的路径就肯定满足了。
当初考场上打了个O(nmlogn)的算法拿了50分。。由于蜜汁处理方法所以并不能拿60分。。
Summary
以上思路可以注意到:1. 在二分答案最优化时,关于判定是否满足答案,已经已经满足条件的无须理会,直接关注不满足条件,需要进行调整的数据之间的共同点,比如本题显然不满足条件的路径的共同点或关键点就是路径交上的边。
2. 树上问题优先考虑列上,再套用LCA和拓扑排序(边上问题,本题就是边上的)或是树链剖分(点上问题)转化到树上。
Code
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define FOR(i,j,k) for(i=j;i<=k;++i) int read() { int s = 0, f = 1; char ch = getchar(); for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') f = -1; for (; '0' <= ch && ch <= '9'; ch = getchar()) s = s * 10 + ch - '0'; return s * f; } const int N = 300005, M = N * 2, K = 18; int h , p[M], v[M], w[M], q[M], cnt = 0; int fa [K + 1], dep , dis ; int mx[M], my[M], mlen[M], mlca[M], m, n; int topo , tnum = 0, stk , flag ; void add(int a, int b, int c) { p[++cnt] = h[a]; v[cnt] = b; w[cnt] = c; h[a] = cnt; } int dfs(int x) { int i, top = 0; stk[++top] = x; while (top) { topo[++tnum] = x = stk[top--]; FOR(i,1,K) fa[x][i] = fa[fa[x][i - 1]][i - 1]; for (i = h[x]; i; i = p[i]) if (v[i] != fa[x][0]) { fa[v[i]][0] = x; q[v[i]] = w[i]; dep[v[i]] = dep[x] + 1; dis[v[i]] = dis[x] + w[i]; stk[++top] = v[i]; } } } int lca(int x, int y) { if (dep[x] < dep[y]) swap(x, y); int t = dep[x] - dep[y], i; FOR(i,0,K) if (t & (1 << i)) x = fa[x][i]; for (i = K; ~i; --i) if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i]; return x == y ? x : fa[x][0]; } bool judge(int mid) { int i, delta = 0, num = 0; FOR(i,1,n) flag[i] = 0; FOR(i,1,m) if (mlen[i] > mid) { flag[mx[i]]++; flag[my[i]]++; flag[mlca[i]] -= 2; delta = max(delta, mlen[i] - mid); ++num; } if (!num) return true; for (i = tnum; i; --i) flag[fa[topo[i]][0]] += flag[topo[i]]; FOR(i,1,n) if (flag[i] == num && q[i] >= delta) return true; return false; } int main() { int i, a, b, c, ans = 0; n = read(); m = read(); FOR(i,2,n) { a = read(); b = read(); c = read(); add(a, b, c); add(b, a, c); } dfs(1); a = b = 0; FOR(i,1,m) { mx[i] = read(); my[i] = read(); mlca[i] = lca(mx[i], my[i]); mlen[i] = dis[mx[i]] + dis[my[i]] - 2 * dis[mlca[i]]; b = max(b, mlen[i]); } while (a <= b) { c = a + b >> 1; if (judge(c)) b = c - 1, ans = c; else a = c + 1; } printf("%d", ans); return 0; }
[NOIP 2015 Day2]运输计划
公元 2044 年,人类进入了宇宙纪元。L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球。
小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之间不会产生任何干扰。
为了鼓励科技创新, L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。
在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后,这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的物流公司的阶段性工作就完成了。
如果小 P 可以自由选择将哪一条航道改造成虫洞, 试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少?
输入格式
第一行包括两个正整数 n,m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 1 到 n 编号。接下来 n−1 行描述航道的建设情况,其中第 i 行包含三个整数 ai,bi 和 ti,表示第 i 条双向航道修建在 ai 与 bi 两个星球之间,任意飞船驶过它所花费的时间为 ti。数据保证 1≤ai,bi≤n 且 0≤ti≤1000。
接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj 和 vj,表示第 j 个运输计划是从 uj 号星球飞往 vj号星球。数据保证 1≤ui,vi≤n
输出格式
输出文件只包含一个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间。样例一
input
6 3 1 2 3 1 6 4 3 1 7 4 3 6 3 5 5 3 6 2 5 4 5
output
11
explanation
将第 1 条航道改造成虫洞: 则三个计划耗时分别为:11,12,11,故需要花费的时间为 12。将第 2 条航道改造成虫洞: 则三个计划耗时分别为:7,15,11,故需要花费的时间为 15。
将第 3 条航道改造成虫洞: 则三个计划耗时分别为:4,8,11,故需要花费的时间为 11。
将第 4 条航道改造成虫洞: 则三个计划耗时分别为:11,15,5,故需要花费的时间为 15。
将第 5 条航道改造成虫洞: 则三个计划耗时分别为:11,10,6,故需要花费的时间为 11。
故将第 3 条或第 5 条航道改造成虫洞均可使得完成阶段性工作的耗时最短,需要花费的时间为 11。
限制与约定
测试点编号 | n的取值 | m的取值 | 约定 |
---|---|---|---|
1 | =100 | =1 | |
2 | =100 | 第 i 条航道连接 i 号星球与 i+1 号星球 | |
3 | |||
4 | =2000 | =1 | |
5 | =1000 | =1000 | 第 i 条航道连接 i 号星球与 i+1 号星球 |
6 | =2000 | =2000 | |
7 | =3000 | =3000 | |
8 | =1000 | =1000 | |
9 | =2000 | =2000 | |
10 | =3000 | =3000 | |
11 | =80000 | =1 | |
12 | =100000 | ||
13 | =70000 | =70000 | 第 i 条航道连接 i 号星球与 i+1 号星球 |
14 | =80000 | =80000 | |
15 | =90000 | =90000 | |
16 | =100000 | =100000 | |
17 | =80000 | =80000 | |
18 | =90000 | =90000 | |
19 | =100000 | =100000 | |
20 | =300000 | =300000 |
空间限制:256MB
相关文章推荐
- Swift按照数组元素出现的次数及大小排序
- Swift按照数组元素出现的次数及大小排序
- Swift按照数组元素出现的次数及大小排序
- Word Pattern
- javascript实现滚动效果的数字时钟实例
- Hibernate的缓存机制
- Python3.4 获取百度网页源码并保存在本地文件中
- 纯新手向--Linux搭建SVN服务器
- C# Stopwatch详解
- C++STL之string
- linux--时间编程(5)
- C++STL之string
- python源码PyObject简单解析
- PHP中mysql与mysqli函数的区别
- UnicodeEncodeError: 'latin-1' codec can't encode character '\u7537' in position 184: ordinal not in
- Android App应用启动分析与优化
- 鼠标滚轮控制侧边div上下翻动效果
- Windows环境下的NodeJS+NPM+Bower安装配置步骤
- 决策树(二)--C4.5
- java中Map集合的常用遍历方法及HashMap的应用实例