题目背景
NOIP2015 提高组 Day2 T3
题目描述
公元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。
接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj 和 vj ,表示第 j 个运输计划是从 uj 号星球飞往 vj 号星球。
输出格式
共 1 行,包含 1 个整数,表示小P的物流公司完成阶段性工作所需要的最短时间。
样例数据 1
输入
6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
4 5
输出
11
备注
【样例1说明】
将第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。
【数据范围】
所有测试数据的范围和特点如下表所示:
请注意常数因子带来的程序效率上的影响。
解析:
看了半天官方题解感觉一脸懵逼,最后只看懂了二分+树链剖分+差分的做法。
具体做法就是:
1.算出每对和之间的最短距离,树剖可顺带解决。
2.将距离按照降序排序。
3.二分答案。 对路径长度大于mid 的路径全部打上标记并计算大于 mid 的边的条数 k,为了方便可将边的信息压到点上。找出被标记 k 次且最大的那条边,如果条边减去这权值是否小于 mid 就可以下调边界,否则上调。
为什么是取 k 条路径的交呢?
证明:
因为如果只取 k - 1 条那么必然有一个路径是无法被减到 mid 以下。
这样做跑得很快,根本不用考虑常数的问题。
做了这道题才发现以前树剖的板子有点问题。。。
代码:
#include <bits/stdc++.h>
using namespace std;
const int Max=300005;
int n,m,s,ans,tot,l,r,mid;
int first[Max],son[Max],pos[Max],top[Max],depth[Max],father[Max],size[Max],dis[Max],num[Max],len[Max],val[Max],v[Max];
struct shu{int to,next,len;}edge[Max<<1];
struct bian{int x,y,len;}d[Max];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') f=-1,c=getchar();
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void build(int x,int y,int z){edge[++s].next=first[x],first[x]=s,edge[s].to=y,edge[s].len=z;}
inline void dfs1(int point,int fa)
{
size[point]=1,father[point]=fa,depth[point]=depth[fa]+1;
for(int u=first[point];u;u=edge[u].next)
{
int to=edge[u].to;
if(to==fa) continue;
val[to]=edge[u].len,dis[to]=dis[point]+edge[u].len;
dfs1(to,point);
size[point]+=size[to];
if(size[to] > size[son[point]]) son[point]=to;
}
}
inline void dfs2(int point,int fa)
{
if(son[point])
{
pos[son[point]]=++tot,len[tot]=val[son[point]],top[son[point]]=top[point];
dfs2(son[point],point);
}
for(int u=first[point];u;u=edge[u].next)
{
int to=edge[u].to;
if(top[to]) continue;
pos[to]=++tot,top[to]=to;
dfs2(to,point);
}
}
inline int LCA(int x,int y)
{
while(top[x]!=top[y])
{
if(depth[top[x]] < depth[top[y]]) x^=y,y^=x,x^=y;
x=father[top[x]];
}
if(depth[x] > depth[y]) swap(x,y);
return x;
}
inline bool comp(const bian &a,const bian &b){return a.len>b.len;}
inline void solve(int x,int y)
{
while(top[x]!=top[y])
{
if(depth[top[x]] < depth[top[y]]) x^=y,y^=x,x^=y;
num[pos[top[x]]+1]++,num[pos[x]+1]--;
x=father[top[x]];
}
if(x==y) return;
if(depth[x] > depth[y]) x^=y,y^=x,x^=y;
num[pos[x]+1]++,num[pos[y]+1]--;
}
inline int mx(int x,int y){return x < y ? y : x;}
inline int check(int mid)
{
int k=0,x=0,sum=0;
while(d[k+1].len>mid) k++;
if(v[k]) return v[k];
memset(num,0,sizeof(num));
for(int i=1;i<=k;i++) solve(d[i].x,d[i].y);
for(int i=1;i<=n;i++){sum+=num[i];if(sum == k) x=mx(x,len[i]);}
v[k]=x;
return x;
}
inline void print(int x){if(x>9) print(x/10);putchar('0'+x%10);}
int main()
{
n=get_int(),m=get_int();
for(int i=1;i<=n-1;i++)
{
int x=get_int(),y=get_int(),z=get_int();
build(x,y,z),build(y,x,z);
}
top[1]=tot=pos[1]=1;
dfs1(1,0),dfs2(1,0);
for(int i=1;i<=m;i++)
{
d[i].x=get_int(),d[i].y=get_int();
d[i].len=dis[d[i].x]+dis[d[i].y]-2*dis[LCA(d[i].x,d[i].y)];
}
sort(d+1,d+m+1,comp);
l=0,r=d[1].len;
while(l<r)
{
mid=(l+r)>>1;
(d[1].len - check(mid) <= mid) ? r=mid : l=mid+1;
}
print(r);
return 0;
}