Road
Description
给你一棵有N个结点的数。这N个结点都有一个权值为$C[i]$。
询问你两个结点u、v,在这两个结点的最短路径上,选取两个点i、j,且i靠近结点u,j靠近结点v。让你计算$Max(c[j]-c[i],0)$
Input
输入第一行有一个整数N(1≤n≤50000)
接下来有N 行,每行一个整数Ci(1≤Ci≤50000)
再接下来有N-1 行,每行两个整数x,y(1≤x,y≤50000),表示x 和y 之间有一条边。
接下来有一个整数M,表示有M 个询问。
然后M 行,每行两个整数,x,y(1≤x,y≤50000)询问$Max(c[j]-c[i],0)$
Output
对于每次询问,输出对应最大值结果
Sample Input
1 | 4 |
Sample Output
1 | 2 |
Data Constraint
对于30%的数据, 1≤N,M≤100
对于60%的数据,1≤N,M≤1000
对于100%的数据,1≤N,M≤50000
Analysis
因为是一棵树,所以任意两点的最短路径是唯一的。
1°30% && 60%的数据
对于每一个询问我们可以O(N)的扫一遍整副图,直到找到终点v。并记录下此时经过路径的结点。然后在O(3*N)的扫一遍得到从结点u到结点i的最小值$(\sum_{i=u}^v A[i] = Min(C[i]))$,以及从结点i到结点v的最大值$(\sum_{i=v}^u B[i] = Max(C[i]))$,最后只需要每一个结点对应扫一遍就行了计算最大值即可。$(\sum_{i=u}^v Ans = Max(B[i]-A[i]))$。这样做的时间复杂度就是$O(MN)$。
还有一种是这种算法的改进。
就是充分利用树的特点,先dfs预处理一遍所有结点到根节点的深度,在一步步向上跳。直到调到他们的LCA。后续的答案计算和前面一样。时间复杂度虽然也是$O(MN)$,可是当数据是随机生成的时候很有可能会优化到$O(2Mlog(N))$。
详情请见代码1……
2°100%的数据
这种涉及到路径的问题肯定会与LCA有关,LCA最快的在线做法就是倍增。
但是倍增在计算是需要合并两个块,所以我们可以考虑一下,怎样合并两个块。
对于一个块就是一个有顺序的结点集合,它需要存放四个值,即:
- 块中所有结点的最小值 (buy)
- 块中所有结点的最大值 (sell)
- 先最小值后最大值的差的绝对值的最大值(Max(bs))
- 先最大值后最小值的差的绝对值的最大值(Min(sb))
我们发现当维护了这四个值后我们就可以进行块合并操作了。
假设要合并的块分别为A和B,合并后的块为C。
- $C.buy = Min(A.buy,B.buy)$
- $C.sell = Max(A.sell,B.sell)$
- $C.bs = Max(A.bs,B.bs,B.sell-A.buy)$
- $C.sb = Max(A.sb,B.sb,A.sell-B.buy)$
然后倍增的到LCA后我们只需要知道$\sum_{i=u}^{LCA}$的块与$\sum_{i=v}^{LCA}$的块合并后即可知道答案。
同理我们也可以用tarjan离线LCA的方法来解决这个问题,这样时间复杂度就是$O(N)$。
详情请见代码2……
****
下面说一下具体的程序实现的小问题
对于离线的tarjan算法,因为用到了并查集,所以我们可以对每一个节点,维护它向上的块,维护的大小取决于当前并查集的大小,这样就能完成从$\sum_{i=u}^{LCA}$的块维护,对于另一边我们可以在(u,v)的LCA上打一个标记,当tarjan遍历回到了LCA后。在进行合并。
可以对照这篇博客中的图看一看:http://blog.csdn.net/hnust_xiehonghao/article/details/9109295
****
Code
代码1:
1 | uses math; |
代码2:
1 | #include <cstdio> |