bzoj4326 & UOJ150【NOIP2015】运输计划
原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=4326
http://uoj.ac/problem/150
题意:
公元 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<=300000
题解:
(旧题重做。
以为是像天天爱跑步那样的解法,朝错误的方向想了很久。
偶然由“使最大的最小”想到二分,得解。)
二分最长的时间。
由此转化为不合法的边(大了的)能否通过删一条边,使其合法。
首先如果能通过删一条边,使他们都合法,那么这些路径必然有交边,且这些路径中最大值-交边中最大的权值<=二分的上界。
如何判断交边?查分即可,对任意路径(u,lca,v),c[u]++,c[v]++,c[lca]-=2。
那么一个点的c值就是其和其父亲的连边被多少路径经过。
对于所有c[u]>=cnt(不合法的边数)
只需判断是否存在 u与其父的连边权值>=路径中最大值 - 二分的上界
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=300005;
const int P=19;
int n,m,head[N],to[2*N],nxt[2*N],w[2*N],num=0,anc[N][P+1],c[N],dep[N],dis[N],path[N],cnt=0,flag=0;
void build(int u,int v,int ww)
{
num++;
to[num]=v;
nxt[num]=head[u];
w[num]=ww;
head[u]=num;
}
struct node
{
int x,y,lca;
node(){}
node(int x,int y,int lca):x(x),y(y),lca(lca){}
}a[N];
void dfs(int u,int f)
{
dep[u]=dep[f]+1;
anc[u][0]=f;
for(int i=1;i<P;i++)
anc[u][i]=anc[anc[u][i-1]][i-1];
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==f) continue;
dis[v]=dis[u]+w[i];
path[v]=w[i];
dfs(v,u);
}
}
int getlca(int u,int v)
{
if(dep[u]<dep[v]) swap(u,v);
int d=dep[u]-dep[v];
for(int i=0;d;i++,d>>=1)
if(d&1) u=anc[u][i];
if(u==v) return u;
for(int i=P-1;i>=0;i--)
if(anc[u][i]!=anc[v][i])
{u=anc[u][i]; v=anc[v][i];}
return anc[u][0];
}
int getans(int u,int f,int val,int mx)
{
int sum=c[u];
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==f) continue;
sum+=getans(v,u,val,mx);
}
if(sum>=val&&path[u]>=mx) flag=1;
return sum;
}
bool check(int x)
{
memset(c,0,sizeof(c)); cnt=0; flag=0; int mx=0;
for(int i=1;i<=m;i++)
if(dis[a[i].x]+dis[a[i].y]-2*dis[a[i].lca]>x)
{c[a[i].x]++; c[a[i].y]++; c[a[i].lca]-=2; cnt++; mx=max(mx,dis[a[i].x]+dis[a[i].y]-2*dis[a[i].lca]);}
if(cnt==0) return 1;
int whatever=getans(1,1,cnt,mx-x);
return flag;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int u,v,ww;
scanf("%d%d%d",&u,&v,&ww);
build(u,v,ww);
build(v,u,ww);
}
dfs(1,1);
for(int i=1;i<=m;i++)
{
int x,y,lca;
scanf("%d%d",&x,&y);
lca=getlca(x,y);
a[i]=node(x,y,lca);
}
int lf=0; int rg=300000000;
while(lf+1<rg)
{
int mid=(lf+rg)>>1;
if(check(mid)) rg=mid;
else lf=mid;
}
if(check(lf)) printf("%d\n",lf);
else printf("%d\n",rg);
return 0;
}