洛谷 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

说明

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

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

这道题有点东西。本来是想练手树上差分的,发现很多题解都是用二分+树链剖分写的,看了一下,树链剖分、线段树、树状数组(求线段标记次数)、LCA(有的是用tarjan求的路径的长度)。

一开始我看到这道题的时候,看到所有的计划都一起跑,就有点傻了,一起跑可怎么找,感觉直接暴力找回超时,后来算了一下,好像可以过。

我的思路就是:一开始的时候,先把所有的路径的长度出来,然后通过dfs把lca的相关东西处理出来,每一条线段的长度先保存一下,然后二分,最长的路径长度为右端点r,二分,每二分一次就遍历一下所有的路径,大于mid长度的,就树上差分保存一下,然后dfs一下就把所有的线段的标记次数找出来了,然后找一下是不是有某条线段被标记的次数为加入的路径的数量,如果有,就说明删掉这条边是可能成立的,最长的路径-这条线段与mid比较一下就可以了,有一点就是标记的次数相同的找最长的那条线段,一开始智障,找到了就跳出去了,wa了。。。

还有一点就是存边的时候数组开二倍,还有一点就是每一条线段的长度保存的时候,不能直接在输入的时候保存,这样会wa,因为用大的数当边的编号是对的,假设1和5相连,5和3相连,一开始1和5相连长度为3,输入的时候,我存的是vv[5]=3;但是等到我存5和3长度为5的时候,vv[5]=5,就不对了。soga,嘎嘎。

测评姬有点情绪不稳,相同的代码我第一次交有一个样例点tle,再交一次一模一样的,过了。。。试了好几次,网卡的时候就容易tle。。。

代码:

  1 //洛谷 P2680 运输计划 二分+树上差分(边覆盖)
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<cmath>
  6 #include<algorithm>
  7 #include<cstdlib>
  8 using namespace std;
  9 typedef long long ll;
 10 const int maxn=3e5+10;
 11 
 12 struct node{
 13     int to,val,next;
 14 }edge[maxn<<1];
 15 
 16 int p1[maxn],p2[maxn],vv[maxn],maxx=0,ret,num,n,m,cnt,ans;
 17 int head[maxn<<1],sum[maxn],dep[maxn],fa[maxn][30],dis[maxn],len[maxn];
 18 
 19 void add(int x,int y,int v){edge[++cnt].to=y,edge[cnt].val=v,edge[cnt].next=head[x],head[x]=cnt;}
 20 
 21 void dfs(int u,int fath)
 22 {
 23     dep[u]=dep[fath]+1,fa[u][0]=fath;
 24     for(int i=0;fa[u][i];++i) fa[u][i+1]=fa[fa[u][i]][i];
 25     for(int i=head[u];i;i=edge[i].next){
 26         int v=edge[i].to,w=edge[i].val;
 27         if(v!=fath) dis[v]=dis[u]+w,vv[v]=w,dfs(v,u);//vv的dfs一开始写挫了,直接写到输入那里了
 28     }
 29 }
 30 
 31 int LCA(int u,int v)
 32 {
 33     if(dep[u]>dep[v]) swap(u,v);
 34     for(int i=21;i>=0;i--) if(dep[u]<=dep[v]-(1<<i)) v=fa[v][i];
 35     if(u==v) return u;
 36     for(int i=21;i>=0;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
 37     return fa[u][0];
 38 }
 39 
 40 void Dfs(int u,int fath)//标记之后的Dfs
 41 {
 42     for(int i=head[u];i;i=edge[i].next){
 43         int v=edge[i].to;
 44         if(v==fath) continue;
 45         Dfs(v,u);
 46         sum[u]+=sum[v];
 47     }
 48     if(num==sum[u]){//如果标记的次数正好为加的边的数量,说明这一段在所有边上都有
 49         ret=max(ret,vv[u]);//标记次数相同的找长度最大的
 50     }
 51 }
 52 
 53 bool pig(int h)//判断
 54 {
 55     num=0;ret=0; memset(sum,0,sizeof(sum));
 56     for(int i=1;i<=m;i++){
 57         if(len[i]>h){
 58             num++;int lca=LCA(p1[i],p2[i]);
 59             sum[p1[i]]++;sum[p2[i]]++;sum[lca]-=2;
 60         }
 61     }
 62     if(!num) return 1;
 63     Dfs(1,0);return maxx-ret<=h;
 64 }
 65 
 66 int main()
 67 {
 68     scanf("%d%d",&n,&m);
 69     for(int i=1;i<n;i++){
 70         int u,v,w;
 71         scanf("%d%d%d",&u,&v,&w);
 72         add(u,v,w);add(v,u,w);//vv[max(u,v)]=w;
 73     }
 74     dfs(1,0);
 75     for(int i=1;i<=m;i++){
 76         int x,y;
 77         scanf("%d%d",&x,&y);
 78         p1[i]=min(x,y);p2[i]=max(x,y);
 79         int lca=LCA(x,y);
 80         len[i]=dis[x]+dis[y]-2*dis[lca];
 81         maxx=max(maxx,len[i]);
 82     }
 83     int l=0,r=maxx;
 84     while(l<=r){
 85         int mid=(l+r)>>1;//cout<<"mid= "<<mid<<endl;
 86         if(pig(mid))
 87             r=mid-1;
 88         else
 89             l=mid+1;
 90     }
 91     cout<<l<<endl;
 92 
 93 }
 94 /*
 95 6 3
 96 1 2 3
 97 1 6 4
 98 1 3 7
 99 3 4 6
100 3 5 5
101 3 6 
102 2 5 
103 4 5
104 
105 
106 11
107 
108 */

不开心,最近状态不好,铁了一年了。唉,菜是原罪。

原文地址:https://www.cnblogs.com/ZERO-/p/10016109.html