UOJ150 运输计划

时间:2023-03-09 13:31:08
UOJ150 运输计划

运输计划
(transport.cpp/c/pas)
【问题描述】
公元 2044 年,人类进入了宇宙纪元。L 国有 n 个星球,还有 n-1 条 双向 航道,每条航道建立在两个星球之间,这 n-1 条航道 连通 了 L 国的所有星球。
小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 u i 号星球沿 最快 的宇航路径飞行到 v i 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 t j ,并且任意两艘飞船之间 不会 产生任何干扰。
为了鼓励科技创新,L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小P 把某一条航道改造成虫洞,飞船驶过虫洞 不消耗 时间。在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后,这 m 个运输计划会 同时 开始,所有飞船 一起 出发。当这 m 个运输计划 都完成 时,小 P 的物流公司的阶段性工作就完成了。如果小 P 可以 *选择 将哪一条航道改造成虫洞,试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少?
【输入格式】
输入文件名为 transport.in。
第一行包括两个正整数 n、 m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 1 到 n 编号。
接下来 n-1 行描述航道的建设情况,其中第 i 行包含三个整数 a i , b i 和 t i ,表示第i 条双向航道修建在 a i 与 b i 两个星球之间,任意飞船驶过它所花费的时间为 t i 。
接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 u j 和 v j ,表示第 j 个运输计划是从 u j 号星球飞往 v j 号星球。
【输出格式】
输出文件名为 transport.out。
共 1 行,包含 1 个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间。

正解:树链剖分+二分答案+差分

解题报告:

  NOIP2015的原题,还记得这道题当年的考场上打的是倍增的暴力。。。

  首先链剖,并预处理一下每个计划的原始距离。二分一个答案x,找出所有距离大于x的计划,单独处理。显然我们需要使这些计划的总时间都要变少,那么我们必须要找出一条边被这所有的超时的计划所包含,并尽可能的使这条边的权值大,这样的话,我们可以考虑把边被经过的次数差分,所有计划求lca的时候顺便差分一下,标记一下经过的边。最后一遍统计每条边的经过次数,如果刚好等于超时的计划,我们才考虑这条边是否可以产生贡献,如果至少有一个计划不满足,无论如何不可能。最后只需要比较一下这条边能否使时间最大的计划完成即可完成可行性判断。

  代码如下:

 //It is made by jump~
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <ctime>
using namespace std;
typedef long long LL;
#define RG register
const int MAXN = ;
const int MAXM = ;
int n,m,ecnt,Max,ans,cnt;
int first[MAXN],to[MAXN*],next[MAXN*],w[MAXN*];
int deep[MAXN],top[MAXN],size[MAXN],son[MAXN],father[MAXN],id[MAXN],sum[MAXN],quan[MAXN];
int b[MAXN];//差分数组,统计经过某条边的次数
int l,r,root;
struct fight{
int u,v;
int dis;
}a[MAXM]; inline int getint()
{
RG int w=,q=; char c=getchar();
while((c<'' || c>'') && c!='-') c=getchar(); if(c=='-') q=,c=getchar();
while (c>='' && c<='') w=w*+c-'', c=getchar(); return q ? -w : w;
} inline void dfs(RG int x,RG int fa){
size[x]=;
for(RG int i=first[x];i;i=next[i]) {
RG int v=to[i]; if(v==fa) continue;
quan[v]=w[i];
deep[v]=deep[x]+; father[v]=x; dfs(v,x); size[x]+=size[v];
if(size[v]>=size[son[x]]) son[x]=v;
}
} inline void dfs2(RG int x,RG int fa){
id[x]=++ecnt; sum[ecnt]=sum[ecnt-]+quan[x];
if(son[x]) top[son[x]]=top[x],dfs2(son[x],x);
for(RG int i=first[x];i;i=next[i]) {
RG int v=to[i]; if(v==fa || v==son[x]) continue;
top[v]=v; dfs2(v,x);
}
} inline void lca(RG int now){
RG int x=a[now].u,y=a[now].v;
RG int f1=top[x],f2=top[y];
while(f1!=f2) {
if(deep[f1]<deep[f2]) swap(f1,f2),swap(x,y);
a[now].dis+=sum[id[x]]-sum[id[f1]-];
x=father[f1]; f1=top[x];
}
if(deep[x]<deep[y]) swap(x,y);
a[now].dis+=sum[id[x]]-sum[id[son[y]]-];//应该是son[y],因为到y为止是y连了y在内的
} inline void update(RG int now){
RG int x=a[now].u,y=a[now].v;
RG int f1=top[x],f2=top[y];
while(f1!=f2) {
if(deep[f1]<deep[f2]) swap(f1,f2),swap(x,y);
b[id[f1]]++; b[id[x]+]--;
x=father[f1]; f1=top[x];
}
if(deep[x]<deep[y]) swap(x,y);
b[id[son[y]]]++; b[id[x]+]--;//+1
} inline bool check(RG int x){
cnt=;//统计不能在规定时间内完成的
memset(b,,sizeof(b));
RG int cost=;
for(RG int i=;i<=n;i++) {
if(a[i].dis>x) {
cnt++; cost=max(cost,a[i].dis-x);
update(i);
}
}
RG int zuida=,now=;
for(RG int i=;i<=n;i++) {//讨论的是结点编号,而不是结点本身!
now+=b[i];
if(now==cnt) {//必须是覆盖了所有,否则的话就没有用
zuida=max(sum[i]-sum[i-],zuida);
}
}
if(zuida>=cost) return true;
return false;
} inline void work(){
//srand(time(NULL));
n=getint(); m=getint(); RG int x,y,z;
for(RG int i=;i<n;i++) {
x=getint(); y=getint(); z=getint(); Max+=z;
next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z;
next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x; w[ecnt]=z;
}
//root=rand()%n+1;
root=n/;
deep[root]=; dfs(root,);
ecnt=; top[root]=root; dfs2(root,); RG int mid;
for(RG int i=;i<=m;i++) a[i].u=getint(),a[i].v=getint(),lca(i);
l=; r=Max;
ans=;
while(l<=r) {
mid=(l+r)/;
if(check(mid)) ans=mid,r=mid-;
else l=mid+;
}
printf("%d",ans);
} int main()
{
work();
return ;
}