luogu P2680 运输计划 (二分答案+树上差分)
题目背景
公元 20442044 年,人类进入了宇宙纪元。
题目描述
公元20442044 年,人类进入了宇宙纪元。
L 国有 nn 个星球,还有 n-1n−1 条双向航道,每条航道建立在两个星球之间,这 n-1n−1 条航道连通了 LL 国的所有星球。
小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 u_iui 号星球沿最快的宇航路径飞行到 v_ivi 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 jj,任意飞船驶过它所花费的时间为 t_jtj,并且任意两艘飞船之间不会产生任何干扰。
为了鼓励科技创新, LL 国国王同意小 PP 的物流公司参与 LL 国的航道建设,即允许小PP 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。
在虫洞的建设完成前小 P 的物流公司就预接了 mm 个运输计划。在虫洞建设完成后,这 mm 个运输计划会同时开始,所有飞船一起出发。当这 mm 个运输计划都完成时,小 PP 的物流公司的阶段性工作就完成了。
如果小 PP 可以自由选择将哪一条航道改造成虫洞, 试求出小 PP 的物流公司完成阶段性工作所需要的最短时间是多少?
输入输出格式
输入格式:
第一行包括两个正整数 n, mn,m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 11 到 nn 编号。
接下来 n-1n−1 行描述航道的建设情况,其中第 ii 行包含三个整数 a_i, b_iai,bi 和 t_iti,表示第 ii 条双向航道修建在 a_iai 与 b_ibi两个星球之间,任意飞船驶过它所花费的时间为 t_iti。数据保证 1 \leq a_i,b_i \leq n1≤ai,bi≤n 且 0 \leq t_i \leq 10000≤ti≤1000。
接下来 mm 行描述运输计划的情况,其中第 jj 行包含两个正整数 u_juj 和 v_jvj,表示第 jj 个运输计划是从 u_juj 号星球飞往 v_jvj号星球。数据保证 1 \leq u_i,v_i \leq n1≤ui,vi≤n
输出格式:
一个整数,表示小 PP 的物流公司完成阶段性工作所需要的最短时间。
输入输出样例
说明
所有测试数据的范围和特点如下表所示
请注意常数因子带来的程序效率上的影响。
思路:
很明显答案是递增的,我们可以直接二分答案,设最短时间为t,那么航线时间大于t的我们就把他放到差分数组里,然后在差分数组里找到所有链公有的权值最大的边权,看减去这条边后所有航线是否都小于等于之前假设的最短时间t,.
这道题注意要用读入挂,要不一直TLE第13个样例。。。。
实现代码:
#include<bits/stdc++.h> using namespace std; const int M = 3e5 + 10; int p[M][22],w[M],dep[M],cnt,head[M],n,ans,dist[M],num,mx,m,sum[M]; int len[M],u[M],v[M],lc[M],r,l; struct node{ int to,next,w; }e[M<<1]; void Add(int u,int v,int w){ e[++cnt].to = v;e[cnt].next = head[u];e[cnt].w = w,head[u] = cnt; } void dfs(int u,int fa,int deep){ dep[u] = deep; p[u][0] = fa; for(int i = head[u];i;i=e[i].next){ int v = e[i].to; if(v == fa) continue; dist[v] = dist[u] + e[i].w; w[v] = e[i].w; dfs(v,u,deep+1); } } void get_fa(){ for(int j = 1;(1<<j)<=n;j++) for(int i = 1;i <= n;i ++) p[i][j] = p[p[i][j-1]][j-1]; } int lca(int a,int b){ if(dep[a] > dep[b]) swap(a,b); int h = dep[b] - dep[a]; for(int i = 0;(1<<i)<=h;i++){ if((1<<i)&h) b = p[b][i]; } if(a != b){ for(int i = 20;i >= 0;i --){ if(p[a][i] != p[b][i]){ a = p[a][i]; b = p[b][i]; } } a = p[a][0]; } return a; } void dfs1(int u,int fa){ for(int i = head[u];i;i=e[i].next){ int v = e[i].to; if(v == fa) continue; dfs1(v,u); sum[u] += sum[v]; } if(sum[u] == num) mx = max(mx,w[u]); } bool check(int x){ int k = 0; num = 0; mx = 0; memset(sum,0,sizeof(sum)); for(int i = 1;i <= m;i ++){ if(len[i] > x){ sum[u[i]]++; sum[v[i]]++; sum[lc[i]] -= 2; num++; k = max(k,len[i]); } } dfs1(1,0); if(k - x <= mx) return 1; else return 0; } int read(){ char c;int num,f=1; while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0'; while(c=getchar(), isdigit(c))num=num*10+c-'0'; return f*num; } inline bool read(int &num) { char in;bool IsN=false; in=getchar(); if(in==EOF) return false; while(in!='-'&&(in<'0'||in>'9')) in=getchar(); if(in=='-'){ IsN=true;num=0;} else num=in-'0'; while(in=getchar(),in>='0'&&in<='9'){ num*=10,num+=in-'0'; } if(IsN) num=-num; return true; } int main() { int a,b,c; read(n); read(m); for(int i = 1;i < n;i ++){ read(a);read(b);read(c); Add(a,b,c); Add(b,a,c); } dfs(1,0,1); get_fa(); for(int i = 1;i <= m;i ++){ read(u[i]); read(v[i]); lc[i] = lca(u[i],v[i]); len[i] = dist[u[i]] + dist[v[i]] - 2*dist[lc[i]]; r = max(r,len[i]); } int k = 0; while(l <= r){ int mid = (l + r) >> 1; if(check(mid)){ k = mid; r = mid-1; } else l = mid+1; } printf("%d\n",k); }