运输计划
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了运输计划相关的知识,希望对你有一定的参考价值。
版权声明:本文为博主原创文章,未经博主允许不得转载。
传送门:运输计划
题目背景
公元 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
说明
所有测试数据的范围和特点如下表所示
请注意常数因子带来的程序效率上的影响。
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=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 }
以上是关于运输计划的主要内容,如果未能解决你的问题,请参考以下文章