洛谷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 的物流公司完成阶段性工作所需要的最短时间。
输入输出样例
说明
所有测试数据的范围和特点如下表所示
请注意常数因子带来的程序效率上的影响。
思路:二分查找答案,二分判断条件是这条路径的长度都小于mid或者大于mid的路径用差分数组维护后求出每条线段被走过几次,走过次数等于大于mid的路径数的就是可以被更改的线段,然后再其中找到最大的那条,用最长路径 - 这个值,如果小于等于mid则成立。
如果TLE了一个样例,那就O2+多交几次,网速快的话好像能AC。
什么都不说了,都在代码里。
1 ///用vector存图比前象星要慢 2 #include<cstdio> 3 #include<string.h> 4 #include<algorithm> 5 using namespace std; 6 inline int scan() 7 { 8 int x=0,c=1; 9 char ch=' '; 10 while((ch>'9'||ch<'0')&&ch!='-')ch=getchar(); 11 while(ch=='-')c*=-1,ch=getchar(); 12 while(ch<='9'&&ch>='0')x=x*10+ch-'0',ch=getchar(); 13 return x*c; 14 } 15 const int maxn=3e5+5; 16 const int maxnbits=20; 17 int n,m,num,ret,Max,tol; 18 int depth[maxn],father[maxn][maxnbits],lg[maxn],dis[maxn],len[maxn],sum[maxn],head[maxn<<1]; 19 struct edge{ 20 int to; 21 int next; 22 int w; 23 }e[maxn<<1]; 24 struct node{ 25 int x,y; 26 }nd[maxn]; 27 void add(int s,int t,int w){ 28 tol++; 29 e[tol].to=t; 30 e[tol].next=head[s]; 31 e[tol].w=w; 32 head[s]=tol; 33 } 34 int v[maxn]; 35 void dfs(int nowp,int fa){ 36 depth[nowp]=depth[fa]+1; 37 father[nowp][0]=fa; 38 for(int i=1;i<=lg[depth[nowp]]+1;i++){ 39 father[nowp][i]=father[father[nowp][i-1]][i-1]; 40 } 41 for(int i=head[nowp];i;i=e[i].next){ 42 int to=e[i].to; 43 if(to!=fa){ 44 dis[to]=dis[nowp]+e[i].w; 45 v[to]=e[i].w; 46 dfs(to,nowp); 47 } 48 } 49 } 50 int LCA(int a,int b){ 51 if(depth[a]<depth[b]){ 52 swap(a,b); 53 } 54 while(depth[a]!=depth[b]){ 55 a=father[a][lg[depth[a]-depth[b]]]; 56 } 57 if(a==b) return a; 58 for(int i=lg[depth[a]];i>=0;i--){ 59 if(father[a][i]!=father[b][i]){ 60 a=father[a][i]; 61 b=father[b][i]; 62 } 63 } 64 return father[a][0]; 65 } 66 void DFS(int nowp,int fa){ 67 for(int i=head[nowp];i;i=e[i].next){ 68 int to=e[i].to; 69 if(to!=fa){ 70 DFS(to,nowp); 71 sum[nowp]+=sum[to];///从叶子节点进行求前缀和 72 } 73 } 74 if(sum[nowp]==num){///sum[nowp]是nowp点的边被num条路径走过几次,如果走过次数等于num,则可对这条边进行更改 75 ret=max(ret,v[nowp]);///记录可更改最大边 76 } 77 } 78 bool check(int mid){ 79 num=0; 80 ret=0; 81 memset(sum,0,sizeof(sum)); 82 for(int i=0;i<m;i++){ 83 if(len[i]>mid){///这条路径是否比mid大,如果大,则可对其的某跳边进行更改 84 num++; 85 int lca=LCA(nd[i].x,nd[i].y); 86 sum[nd[i].x]++,sum[nd[i].y]++,sum[lca]-=2;///差分记录这条路径的所有边可进行更改 87 } 88 } 89 if(!num) return true;///如果没有比mid大的路径,那么它就是合法路径(mid) 90 DFS(1,0);///否则对能更改的路径进行更改 91 return Max-ret<=mid;///如果最长路径-能更改的最大边小于等于mid,则这个mid合法 92 } 93 int main(){ 94 lg[0]=-1; 95 for(int i=1;i<maxn-2;i++){ 96 lg[i]=lg[i>>1]+1; 97 } 98 int a,b,t; 99 int l=0,r=-1; 100 n=scan(); 101 m=scan(); 102 for(int i=0;i<n-1;i++){ 103 a=scan(); 104 b=scan(); 105 t=scan(); 106 add(a,b,t); 107 add(b,a,t); 108 } 109 dfs(1,0); 110 for(int i=0;i<m;i++){ 111 a=scan(); 112 b=scan(); 113 nd[i]={a,b}; 114 int lca=LCA(a,b); 115 len[i]=dis[a]+dis[b]-2*dis[lca];///计算a-b的距离 116 r=max(r,len[i]);///查找最大的路径长度,做二分最大值 117 Max=r; 118 } 119 int ans=0; 120 while(l<=r){ 121 int mid=(l+r)>>1; 122 if(check(mid)){///如果这个mid符合条件,则找更小的,看能否找到符合条件的 123 r=mid-1; 124 ans=mid; 125 }else{ 126 l=mid+1; 127 } 128 } 129 printf("%d ",ans); 130 return 0; 131 }