[NOIP2015]运输计划https://www.luogu.org/problemnew/show/P2680
题目背景
公元 \(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\) 号星球。
输出格式:
输出共\(1\)行,包含\(1\)个整数,表示小\(P\)的物流公司完成阶段性工作所需要的最短时间。
输入样例:
6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
4 5
输出样例:
11
说明
\(n,m<=300000\)
\(1<=a_i,b_i,u_j,v_j<=n\)
\(t_i<=1000\)
请注意常数因子带来的程序效率上的影响。
由题面信息可知,我们要删除一条边,使所有计划用时的最大值最小.
不难猜出是要二分答案的.
左端点\(L=0\),右段点\(R=\)所有计划中用时的最大值\(Maxlen\).
因此先预处理出每个运输计划的用时[求LCA]
二分出一个\(mid\)
那么所有用时大于\(mid\)的计划都是不合法的,这些路径必须作出改动,因此删掉的边应为它们的公共边.
如果树剖维护每条边被不合法计划经过的次数,
那么被删的那条边一定被每个不合法计划都经过
找出这些边
对于某条边,设边权为\(val\),并检验
\[Maxlen-val<=mid\]
如果有一条边能满足条件,则有解,返回\(true\)
否则返回\(false\)
建议:
树剖用树状数组,线段树常数大卡掉\(5\)分,树状数组开\(O2\)可过.
#define RG register
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=3e5+5;
inline int read()
{
RG int x=0,w=1;RG char ch=getchar();
while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)w=-1;ch=getchar();}
while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar();
return x*w;
}
int n,m,ct,cnt,L,R,Ans;
int last[N],dep[N],top[N],son[N],dfn[N],size[N],f[N][25],dist[N][25],sum[N];
struct edge{int to,next,w;}e[N<<1];
struct node{int val,u,v;}lca[N];
inline bool cmp(node a,node b){return a.val<b.val;}
inline int lowbit(int x){return (-x)&x;}
inline void insert(int u,int v,int w)
{
e[++cnt]=(edge){v,last[u],w};last[u]=cnt;
e[++cnt]=(edge){u,last[v],w};last[v]=cnt;
}
void dfs1(int now)
{
size[now]=1;
for(int i=last[now];i;i=e[i].next)
{
int v=e[i].to;
if(v==f[now][0])continue;
dep[v]=dep[now]+1;
f[v][0]=now;
dist[v][0]=e[i].w;
dfs1(v);
size[now]+=size[v];
if(size[v]>size[son[now]])son[now]=v;
}
}
void dfs2(int now,int Top)
{
top[now]=Top;dfn[now]=++ct;
if(son[now])dfs2(son[now],Top);
for(RG int i=last[now];i;i=e[i].next)
{
RG int v=e[i].to;
if(v==f[now][0]||v==son[now])continue;
dfs2(v,v);
}
}
inline void init()
{
dep[1]=1;
dfs1(1);
dfs2(1,1);
for(RG int j=1;j<=20;j++)
for(RG int i=1;i<=n;i++)
{
f[i][j]=f[f[i][j-1]][j-1];
dist[i][j]=dist[i][j-1]+dist[f[i][j-1]][j-1];
}
}
inline int Lca(int x,int y)
{
RG int Sum=0;
if(dep[y]>dep[x])swap(x,y);
for(RG int i=20;i>=0;i--)if(f[x][i]&&dep[f[x][i]]>=dep[y])Sum+=dist[x][i],x=f[x][i];
if(x==y)return Sum;
for(RG int i=20;i>=0;i--)
if(f[x][i]&&f[y][i]&&f[x][i]!=f[y][i])
{
Sum+=dist[x][i]+dist[y][i];
x=f[x][i];y=f[y][i];
}
Sum+=dist[x][0]+dist[y][0];
return Sum;
}
inline void Modify(int x,int k)
{
while(x<=n)sum[x]+=k,x+=lowbit(x);
}
inline int Query(int x)
{
RG int Ans=0;
while(x)Ans+=sum[x],x-=lowbit(x);
return Ans;
}
inline void Modify_Tree(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
Modify(dfn[top[x]],1);Modify(dfn[x]+1,-1);
x=f[top[x]][0];
}
if(dep[x]>dep[y])swap(x,y);
if(x!=y)Modify(dfn[x]+1,1),Modify(dfn[y]+1,-1);
}
inline bool check(int k)
{
memset(sum,0,sizeof(sum));
RG int Num=0;
for(RG int i=m;i>=1;i--)
{
if(lca[i].val>k)Num++,Modify_Tree(lca[i].u,lca[i].v);
else break;
}
for(RG int i=1;i<=n;i++)if(Query(dfn[i])==Num&&lca[m].val-dist[i][0]<=k)return true;
return false;
}
int main()
{
n=read();m=read();
for(RG int i=1;i<n;i++)
{
RG int a=read(),b=read(),c=read();
insert(a,b,c);
}
init();
for(RG int i=1;i<=m;i++)
{
lca[i].u=read();lca[i].v=read();
lca[i].val=Lca(lca[i].u,lca[i].v);
}
sort(lca+1,lca+m+1,cmp);
R=lca[m].val;
while(L<=R)
{
RG int mid=(L+R)>>1;
if(check(mid))Ans=mid,R=mid-1;
else L=mid+1;
}
printf("%d\n",Ans);
return 0;
}