洛谷P2680 运输计划(LCA+树上差分)
首先考虑如果一段运输可以在t的时间内完成,那么一定是可以在大于t的时间内完成的,服从了单调性,于是我们这里可以考虑一下二分查找距离最大话重复的边。
我们假设时间确定,原问题变成一个判定性问题:是否可以通过去掉一条边,使所有路径的总长度在 t以内。
此时去掉所有长度大于 t的路径上的最长公共边一定是最优的。
那怎么找出所有公共边呢?我们可以考虑差分,将每条路径上的所有边加1,那么经一次这条边就会加1,假设一共有s条路径,如果有重边,那么每走一次都会经过他一遍,最后经过了s次,于是只需要判断每条边上的总和是否等于路径总数即可。
那么我们的问题是就变成了求树上某一条路径的和,以及给某个路径加上相同数呢。
这里可以利用前缀和还有差分。
对于距离,假设要求x,y之间的距离,我们先预处理出每个点到根节点的距离dis[x],然后再利用x到y的距离一定是经过了最近公共祖先的,于是由前缀和
d(x,y)=dis[x]-dis[lca(x,y)]+dis[y]-dis[lca(x,y)],求LCA的方式有很多,这里采用的是倍增的方式。
对于每条路径加上相同数,我们利用差分,设差分数组为sum[x](表示的是1到x),那么要把x,y的路径都加上1,就相当于把x到lca(x,y)加上1和lca(x,y)到y之一段加上1,于是sum[x]++,sum[y]++,但是sum表示的是由于是根节点到x,于是我们刚刚多加了两次从根节点到lca(x,y),于是最后还需sum[lca(x,y)]-2。
最后就二分中的Check函数,怎么写呢,就是看能不能找到至少一条长为k公共边,使得最长链的长度max(length-k)<=t.
#include<bits/stdc++.h> using namespace std; const int N=3e5+10; int h[N],ne[N*2],e[N*2],w[N*2],idx; int dep[N],f[N][22],dis[N]; int sum[N];//差分数组 pair<int,int> tran[N];//用于存储从x运输到y的位置 int blca[N],seq[N];//用于存储lca(x,y),记录下行走路径 int n,m,cnt; void add(int a,int b,int c) { e[++idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx; } void dfs(int u,int fa)//倍增以及dep和dis的预处理 { seq[++cnt]=u; dep[u]=dep[fa]+1;f[u][0]=fa; for(int i=1;i<=20;i++) f[u][i]=f[f[u][i-1]][i-1]; for(int i=h[u];i;i=ne[i]) { int v=e[i]; if(v==fa)continue; dis[v]=dis[u]+w[i]; dfs(v,u); } } int lca(int u,int v) { if(dep[u]<dep[v])swap(u,v); for(int i=20;i>=0;i--) if(dep[f[u][i]]>=dep[v]) u=f[u][i]; if(u==v)return u; for(int i=20;i>=0;i--) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i]; return f[u][0]; } bool check(int mid) { memset(sum,0,sizeof sum);//每次都要用到 int maxd=0,s=0; for(int i=1;i<=m;i++) { int x=tran[i].first,y=tran[i].second; int p=blca[i]; int d=dis[x]+dis[y]-2*dis[p];//x和y之间的路径 if(d>mid)//如果他们之间的大于我们规定的长度说明是要进行处理的 { sum[x]++,sum[y]++; sum[p]-=2;//差分处理 maxd=max(maxd,d-mid);//d-mid相当于我们给定mid,实际要有d,d-mid是需要我们处理的 s++; } } if(!s)return true;//如果s为0说明没有重边 for(int i=n;i;i--) { int x=seq[i]; sum[f[x][0]]+=sum[x];//构造差分数组,即把x点++的扩散到整个路径上 } for(int i=1;i<=n;i++) if(sum[i]==s&&dis[i]-dis[f[i][0]]>=maxd)//有重边,并且这条边的路径比我们想要删除的大 return true;//我们找到了这个 return false; } int main() { ios::sync_with_stdio(0),cin.tie(0); cin>>n>>m; for(int i=1;i<n;i++) { int a,b,c;cin>>a>>b>>c; add(a,b,c),add(b,a,c); } dfs(1,0); for(int i=1;i<=n;i++) { int a,b;cin>>a>>b; tran[i]={a,b};blca[i]=lca(a,b); } int l=0,r=1e9;//二分最大的那个需要删除了路径 while(l<r) { int mid=l+r>>1; if(check(mid)) r=mid; else l=mid+1; } cout<<r; }

浙公网安备 33010602011771号