洛谷P2680(树上差分+二分)

洛谷P2680:运输计划

题目背景

公元 20442044 年,人类进入了宇宙纪元。

题目描述

公元20442044 年,人类进入了宇宙纪元。

L 国有 nn 个星球,还有 n-1n1 条双向航道,每条航道建立在两个星球之间,这 n-1n1 条航道连通了 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-1n1 行描述航道的建设情况,其中第 ii 行包含三个整数 a_i, b_iai,bi 和 t_iti,表示第 ii 条双向航道修建在 a_iai与 b_ibi 两个星球之间,任意飞船驶过它所花费的时间为 t_iti。数据保证 1 \leq a_i,b_i \leq n1ai,bin 且 0 \leq t_i \leq 10000ti1000。

接下来 mm 行描述运输计划的情况,其中第 jj 行包含两个正整数 u_juj 和 v_jvj,表示第 jj 个运输计划是从 u_juj 号星球飞往 v_jvj号星球。数据保证 1 \leq u_i,v_i \leq n1ui,vin

 

输出格式:

 

一个整数,表示小 PP 的物流公司完成阶段性工作所需要的最短时间。

 

输入输出样例

输入样例#1: 复制
6 3 
1 2 3 
1 6 4 
3 1 7 
4 3 6 
3 5 5 
3 6 
2 5 
4 5
输出样例#1: 复制
11

说明

所有测试数据的范围和特点如下表所示

请注意常数因子带来的程序效率上的影响。

思路:二分查找答案,二分判断条件是这条路径的长度都小于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\n",ans);
130     return 0;
131 }
View Code

 

posted @ 2019-05-16 22:42  Choose_and_be_chosen  阅读(290)  评论(0编辑  收藏  举报