首页 > 代码库 > 运输计划

运输计划

版权声明:本文为博主原创文章,未经博主允许不得转载。

传送门:运输计划

题目背景

公元 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的物流公司完成阶段性工作所需要的最短时间。

输入输出样例

输入样例#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

说明

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

技术分享

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

 

Algorithm:倍增,二分答案

Solution:首先可以用几乎裸的倍增LCA(如果想快点也可以打Tarjan)求出每一个计划在不考虑虫洞的情况下所需要的时间。然后我们可以用一个结构体来记录第i个计划的两个星球,其LCA,以及距离(即时间)。到这步,我开始想的是将所有任务按距离从大到小排序一遍,然后在距离最大的计划上对于每一条边做处理。但是发现,每做一次处理会导致别的计划距离改变,最坏的情况是改变了所有的计划并且还不知道新的距离,这样弄时间复杂度会到O(m*m)。然后换一种思路,最近二分答案用的比较多,想必这道题也可以二分答案(因为其答案性质满足二分的需要)。然后我们可以发现,对于每一个时间t,如果能找到一条包含于所有目前距离>t的任务的边,并且使该边距离为0之后可以使得所有目前距离>t的任务的边的距离都<t,那么时间t是可以完成工作的,因此算法就是一个二分答案加判断。

  另一个问题是如何找公共边。我们可以这样,将每一条边的前驱点视为两端点中深度较大的那一个,那么对于每一条边都有唯一对应的点,反过来也是如此,所以就可以通过记点的被通过次数来表示边的被通过次数,如果被通过次数恰好等于不满足当前时间t的边(不合法边)的总边数,那么该边一定是这些边的公共边,因为在这里我们只对这些不合法的边进行考虑。我们用一个Num[i]表示点i被经过的次数。对于当前所有不符合距离<t的边,我们将它们的两端点的Num值++,其LCA的Num值-=2(为了防止重复累加)。然后从每一个叶子节点开始往上推当前节点的Num值要累加上它所有儿子节点的Num值。因为每一个计划都一定是从深度大的往LCA走,所以可以保证往上推使得每一个点(即相对应边)的Num值无误。最后查找所有边中属于这些不合法边的公共边的边,如果存在一条边使得最大的距离剪去它的距离后<=t,那么可以在t时间内完成所有任务。

Hint:

  二分的时候l=0,r=最大时间+1,一定不要把l=1或r=最大时间

  查找有多少条边不合法的时候一定要手打一个二分,直接O(m)扫一遍肯定会出事

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 using namespace std;
  5 
  6 template <typename Type> inline void Read(Type &in){
  7     Type f=1;char ch=getchar();in=0;
  8     for(;ch>9||ch<0;ch=getchar())if(ch==-)f=-1;
  9     for(;ch>=0&&ch<=9;ch=getchar())in=in*10+ch-0;
 10 }
 11 
 12 const int MAXN = 3*1e5+1;
 13 const int MAX = 20;
 14 
 15 int n,m;
 16 int Pre[MAXN];
 17 
 18 int Head[MAXN<<1],Size;
 19 struct EDGE{
 20     int Next,To,Dis,Number;
 21 }Edge[MAXN<<1];
 22 
 23 void Ins(int Number,int From,int To,int Dis){
 24     Edge[++Size].Next=Head[From];
 25     Head[From]=Size;
 26     Edge[Size].To=To;
 27     Edge[Size].Dis=Dis;
 28     Edge[Size].Number=Number;
 29 }
 30 
 31 int Root;
 32 int Father[MAXN][MAX+1],Dis[MAXN][MAX+1],Deep[MAXN];
 33 bool Vis[MAXN];
 34 
 35 void Dfs(int u){
 36     for(int i=Head[u];i;i=Edge[i].Next){
 37         int v=Edge[i].To;
 38         if(Vis[v])continue;Vis[v]=1;Pre[Edge[i].Number]=v;
 39         Father[v][0]=u;Dis[v][0]=Edge[i].Dis;Deep[v]=Deep[u]+1;
 40         Dfs(v);
 41     }
 42 }
 43 
 44 int LCA(int &Change,int u,int v){
 45     if(Deep[u]<Deep[v])swap(u,v);
 46     int D_Value=http://www.mamicode.com/Deep[u]-Deep[v];
 47     for(int i=0;i<=MAX;i++)
 48         if(D_Value&(1<<i)){
 49             Change+=Dis[u][i];
 50             u=Father[u][i];
 51         }
 52     if(u==v) return u;
 53     for(int i=MAX;i>=0;i--)
 54         if(Father[u][i]!=Father[v][i]){
 55             Change+=Dis[u][i]+Dis[v][i];
 56             u=Father[u][i];v=Father[v][i];
 57         }
 58     Change+=Dis[u][0]+Dis[v][0];
 59     return Father[u][0];
 60 }
 61 
 62 struct Node{
 63     int u,v,Dis,Lca;
 64     bool operator < (Node Tmp) const{return Dis>Tmp.Dis;}
 65 }Data[MAXN];
 66 
 67 int Upper(int Tmp){
 68     int l=1,r=n;
 69     while(l<r-1){
 70         int Mid=(l+r)>>1;
 71         if(Data[Mid].Dis>Tmp) l=Mid;
 72         else r=Mid;
 73     }return Data[r].Dis>Tmp?r:l;
 74 }
 75 
 76 int Num[MAXN];
 77 void Updata(int u){
 78     for(int i=Head[u];i;i=Edge[i].Next){
 79         int v=Edge[i].To;
 80         if(Deep[v]<Deep[u])continue;
 81         Updata(v);
 82         Num[u]+=Num[v];
 83     }
 84 }
 85 
 86 bool Check(int Time){
 87     memset(Num,0,sizeof Num);
 88     int End=Upper(Time),Best=Data[1].Dis-Time;
 89     if(Best<=0) return true;
 90     for(int i=1;i<=End;i++){
 91         Num[Data[i].u]++;Num[Data[i].v]++;
 92         Num[Data[i].Lca]-=2;
 93     }
 94     
 95     Updata(Root);
 96     
 97     for(int i=1;i<2*n;i++)
 98         if(Num[Pre[Edge[i].Number]]==End&&Edge[i].Dis>=Best)
 99             return true;
100     return false;
101 }
102 
103 void Binary(){
104     int l=0,r=3*1e8+1;
105     while(l<r-1){
106         int Mid=(l+r)>>1;
107         if(Check(Mid)) r=Mid;
108         else l=Mid;
109     }
110     printf("%d\n",Check(l)?l:r);
111 }
112 
113 int main(){
114     Read(n);Read(m);
115     
116     for(int i=1,u,v,dis;i<n;i++){
117         Read(u);Read(v);Read(dis);
118         Ins(i,u,v,dis);Ins(i,v,u,dis);
119     }
120     
121     Vis[Root=1]=1;
122     Dfs(Root);
123     for(int j=1;j<=MAX;j++)
124         for(int i=1;i<=n;i++){
125             Father[i][j]=Father[Father[i][j-1]][j-1];
126             Dis[i][j]=Dis[i][j-1]+Dis[Father[i][j-1]][j-1];
127         }
128     
129     for(int i=1;i<=m;i++){
130         Read(Data[i].u);Read(Data[i].v);
131         Data[i].Lca=LCA(Data[i].Dis,Data[i].u,Data[i].v);
132     }
133     sort(1+Data,1+m+Data);
134     
135     Binary();
136     
137     return 0;
138 }

 

  

运输计划