[NOIP2015] 运输计划
题目背景
公元 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 号星球。
输出格式:输出 共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
说明
所有测试数据的范围和特点如下表所示
请注意常数因子带来的程序效率上的影响。
模型:给出一棵树,给出树上的一些路径,要你删除一条边使得最大路径最小
题解:
LCA+二分+树上差分
先求出所有任务路径的长度(O(mlogn))
二分,找出所有大于mid的任务路径,考虑删除一条边使得最大任务路径<=mid
要删的一定是所有任务路径都经过的权值最大的边
树上差分,对于一条任务路径(u,v),cnt[u]+=1,cnt[v]+=1,cnt[lca(u,v)]-=2,再跑一遍dfs找出cnt[x]==任务路径数的边(O(logL*(m+n),L=1000)
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #include<algorithm> 7 #define ll long long 8 #define RG register 9 using namespace std; 10 11 const int maxn = 300010; 12 13 int n,m,mxl,ans=1<<30; 14 int nxt[maxn*2],to[maxn*2],w[maxn*2],h[maxn],e_num; 15 int fa[maxn],dep[maxn],top[maxn],siz[maxn],son[maxn],dist[maxn],way[maxn]; 16 int cnt[maxn],tot,mx,mx1; 17 struct NODE { 18 int x,y,l; 19 }task[maxn]; 20 21 int gi() { 22 int x=0; char ch=getchar(); 23 while(ch<'0' || ch>'9') ch=getchar(); 24 while(ch>='0' && ch<='9') {x=10*x+ch-'0';ch=getchar();} 25 return x; 26 } 27 28 inline void add(int x, int y, int z) {nxt[++e_num]=h[x],to[e_num]=y,w[e_num]=z,h[x]=e_num;} 29 30 inline void dfs1(int u) { 31 siz[u]=1; 32 for(int i=h[u]; i; i=nxt[i]) { 33 int v=to[i]; 34 if(v==fa[u]) continue; 35 fa[v]=u,dep[v]=dep[u]+1,dist[v]=dist[u]+w[i],way[v]=w[i]; 36 dfs1(v); 37 if(siz[v]>siz[son[u]]) son[u]=v; 38 siz[u]+=siz[v]; 39 } 40 } 41 42 inline void dfs2(int u) { 43 if(son[u]) top[son[u]]=top[u],dfs2(son[u]); 44 for(int i=h[u]; i; i=nxt[i]) { 45 int v=to[i]; 46 if(v==fa[u] || v==son[u]) continue; 47 top[v]=v,dfs2(v); 48 } 49 } 50 51 inline int lca(int x, int y) { 52 while(top[x]!=top[y]) { 53 if(dep[top[x]]>dep[top[y]]) x=fa[top[x]]; 54 else y=fa[top[y]]; 55 } 56 if(dep[x]<dep[y]) return x; 57 else return y; 58 } 59 60 inline int dfs3(int u) { 61 for(int i=h[u]; i; i=nxt[i]) { 62 int v=to[i]; 63 if(v==fa[u]) continue; 64 cnt[u]+=dfs3(v); 65 } 66 if(cnt[u]==tot) mx1=max(mx1,way[u]); 67 return cnt[u]; 68 } 69 70 inline bool check(int k) { 71 memset(cnt,0,sizeof(cnt)); 72 mx=mx1=tot=0; 73 for(int i=1; i<=m; i++) { 74 if(task[i].l>k) { 75 mx=max(mx,task[i].l); 76 cnt[task[i].x]+=1,cnt[task[i].y]+=1,cnt[lca(task[i].x,task[i].y)]-=2; 77 tot++; 78 } 79 } 80 dfs3(1); 81 return mx-mx1<=k; 82 } 83 84 int main() { 85 n=gi(),m=gi(); 86 for(RG int i=1; i<n; i++) { 87 int x=gi(),y=gi(),z=gi(); 88 add(x,y,z),add(y,x,z); 89 } 90 dep[1]=1,fa[1]=1,top[1]=1; 91 dfs1(1),dfs2(1); 92 for(RG int i=1; i<=m; i++) { 93 int x=gi(),y=gi(); 94 task[i].x=x,task[i].y=y; 95 task[i].l=dist[x]+dist[y]-2*dist[lca(x,y)]; 96 mxl=max(mxl,task[i].l); 97 } 98 int l=0,r=mxl,mid; 99 l=max(r-1000,0); 100 while(l<=r) { 101 mid=(l+r)>>1; 102 if(check(mid)) ans=min(ans,mid),r=mid-1; 103 else l=mid+1; 104 } 105 printf("%d", ans); 106 return 0; 107 }
总结:
1、树上差分算路径的交
2、从根dfs到叶子,回溯时返回子孙的信息
3、不要无脑设二分上下界,保证正确性的情况下尽量使二分次数变少