即将参加NOIP2019的蒟蒻报道
经过一夜的思考,终于将NOIP2015最难的题解决了,至此NOIP2015全部6题AC
- 运输计划
(transport.cpp/c/pas)
【问题描述】
公元 2044 年,人类进入了宇宙纪元。
L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条
航道连通了 L 国的所有星球。
小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物
流飞船需要从 ui号星球沿最快的宇航路径飞行到 vi号星球去。显然,飞船驶过一条航道
是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之
间不会产生任何干扰。
为了鼓励科技创新,L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小
P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。
在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后,
这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的
物流公司的阶段性工作就完成了。
如果小 P 可以自由选择将哪一条航道改造成虫洞,试求出小 P 的物流公司完成阶段
性工作所需要的最短时间是多少?
【输入格式】
输入文件名为 transport.in。
第一行包括两个正整数 n、m,表示 L 国中星球的数量及小 P 公司预接的运输计划的
数量,星球从 1 到 n 编号。
接下来 n-1 行描述航道的建设情况,其中第 i 行包含三个整数 ai, bi和 ti,表示第
i 条双向航道修建在 ai与 bi两个星球之间,任意飞船驶过它所花费的时间为 ti。
接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj和 vj,表示第 j 个
运输计划是从 uj号星球飞往 vj号星球。
【输出格式】
输出文件名为 transport.out。
共 1 行,包含 1 个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间。
【输入输出样例 1】
transport.in transport.out
6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
4 5
11
见选手目录下的 transport/transport1.in 与 transport/transport1.ans
【输入输出样例 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。
【样例输入输出 2】
见选手目录下的 transport/transport2.in 与 transport/transport2.ans。
【数据规模与约定】
所有测试数据的范围和特点如下表所示
测试点编号 n= m= 约定
1 100 1
2 100 第 i 条航道连接 i 号星球与 i+1 号星球
3
4 2000 1
5 1000 1000
6 2000 2000 第 i 条航道连接 i 号星球与 i+1 号星球
7 3000 3000
8 1000 1000
9 2000 2000
10 3000 3000
11 80000 1
12 100000
13 70000 70000
14 80000 80000 第 i 条航道连接 i 号星球与 i+1 号星球
15 90000 90000
16 100000 100000
17 80000 80000
18 90000 90000
19 100000 100000
20 300000 300000
所有数据 1≤ai,bi,uj,vj≤n,0≤ti≤1000
请注意常数因子带来的程序效率上的影响。
乍一看 m=1,有20分 LCA找到路径上最长的路,删掉即可。
再一看有链,又有20分,可以通过维护一个sum数组,将查询边长的时间复杂度降为O(1),暴力枚举删除每一条边即可
最后理性分析,我们将特殊情况扩展到一般情况,发现求解最大值最小,考虑使用二分,我们接着看如何判断解的可行性,我们可以发现,每次确定一个答案,我们需要找到所有路径长度超过这个答案的计划,找到其中一条公共边,保证这条公共边的权值是所有公共边中最大的那个,减掉路径后,所有超过这个答案的路径长度变短,如果有长度仍然超过的,那么这个答案不可行,否则可行。
那么应该怎样去找公共边呢?我们考虑树上差分,如果覆盖一次这个边,那么将temp[plan[i].start]++,temp[plan[i].end]++,temp[plan[i].lca]-=2,然后在最后从子节点开始累加,最后temp中存入的是每个结点覆盖的次数。找到temp[i]=超过答案的边的个数并且dis[i]-dis[fa[i]]>=ans,那么答案可行,否则不可行
所以代码如下:
#include <bits/stdc++.h>
using namespace std;
#define maxn 300005
int f[maxn][25],head[maxn],deep[maxn],num[maxn],dis[maxn],tot[maxn];
int cnt=0,n,m,summ=0;
struct node
{
int to,next,w;
}e[maxn*2];
struct Node
{
int start,end,lca,diss;
}plan[maxn];
inline int read()
{
int w=1,s=0; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')w=-1; ch=getchar();}
while(ch>='0' && ch<='9'){s=s*10+ch-'0'; ch=getchar();}
return w*s;
}
void add(int x,int y,int v)
{
cnt++;
e[cnt].to=y;
e[cnt].next=head[x];
e[cnt].w=v;
head[x]=cnt;
}
void dfs(int u,int fa,int d)
{
summ++;
deep[u]=deep[fa]+1;
num[summ]=u;
dis[u]=dis[fa]+d;
f[u][0]=fa;
for(int i=1;i<=22;i++) f[u][i]=f[f[u][i-1]][i-1];
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to,dist=e[i].w;
if(v==fa) continue;
dfs(v,u,dist);
}
}
int LCA(int x,int y)
{
if(deep[x]<deep[y]) swap(x,y);
for(int i=22;i>=0;i--)
{
if(deep[f[x][i]]>=deep[y]) x=f[x][i];
}
if(x==y) return x;
for(int i=22;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i]; y=f[y][i];
}
}
return f[x][0];
}
bool check(int mid)
{
int s=0,ans=0;
memset(tot,0,sizeof(tot));
for(int i=1;i<=m;i++)
{
if(plan[i].diss>mid)
{
tot[plan[i].start]++; tot[plan[i].end]++; tot[plan[i].lca]-=2;
ans=max(ans,plan[i].diss-mid);
s++;
}
}
if(s==0) return true;
for(int i=n;i>=1;i--) tot[f[num[i]][0]]+=tot[num[i]];
for(int i=1;i<=n;i++) if(tot[i]==s && dis[i]-dis[f[i][0]]>=ans) return true;
return false;
}
int main()
{
n=read(); m=read();
int sum=0;
for(int i=1;i<n;i++)
{
int a=read(),b=read(),c=read();
add(a,b,c); add(b,a,c);
sum+=c;
}
dfs(1,0,0);
for(int i=1;i<=m;i++)
{
plan[i].start=read(); plan[i].end=read();
plan[i].lca=LCA(plan[i].start,plan[i].end);
plan[i].diss=dis[plan[i].start]+dis[plan[i].end]-2*dis[plan[i].lca];
}
int l=0,r=sum;
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid)==true) r=mid-1;
else l=mid+1;
}
printf("%d",l);
return 0;
}