1. 程式人生 > >洛谷P2680 運輸計劃——樹上差分

洛谷P2680 運輸計劃——樹上差分

urn set edge 是否 show clu 路線 con 好高興

題目:https://www.luogu.org/problemnew/show/P2680

久違地1A了好高興啊!

首先,要最大值最小,很容易想到二分;

判斷當前的 mid 是否可行,需要看看有沒有去掉一條邊使滿足的方案;

這就需要樹上差分來找出每條邊被幾個超過 mid 的路線覆蓋;

若有一條邊正好被所有超過 mid 的路線覆蓋,且去掉它之後最大的路線也能滿足,就是可行的。

代碼如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int const maxn=3e5+5
; int n,m,head[maxn],ct,cnt,st[maxn],ed[maxn],tag[maxn],lca[maxn],len[maxn]; int h[maxn],ans,mx,f[maxn][20],dep[maxn],l,r,mid; bool flag; struct N{ int to,next,w; N(int t=0,int n=0,int w=0):to(t),next(n),w(w) {} }edge[maxn<<1]; void add(int x,int y,int z){edge[++ct]=N(y,head[x],z); head[x]=ct;}
void init(int x,int fa) { f[x][0]=fa; dep[x]=dep[fa]+1; for(int i=1;i<=18;i++)f[x][i]=f[f[x][i-1]][i-1]; for(int i=head[x],u;i;i=edge[i].next) if((u=edge[i].to)!=fa) h[u]=h[x]+edge[i].w, init(u,x); } int LCA(int x,int y) { if(dep[x]<dep[y])swap(x,y); int k=dep[x]-dep[y];
for(int i=0;i<=18;i++) if(k&(1<<i))x=f[x][i]; for(int i=18;i>=0;i--) if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; if(x==y)return x; return f[x][0]; } void dfs(int x) { for(int i=head[x],u;i;i=edge[i].next) { if((u=edge[i].to)==f[x][0])continue; dfs(u); if(flag)return; if(tag[u]==cnt && mx-edge[i].w<=mid)flag=1; tag[x]+=tag[u]; } } bool ck() { cnt=0; flag=0; memset(tag,0,sizeof tag); for(int i=1;i<=m;i++) if(len[i]>mid) { tag[st[i]]++; tag[ed[i]]++; tag[lca[i]]-=2; cnt++; } dfs(1); return flag; } int main() { scanf("%d%d",&n,&m); for(int i=1,x,y,z;i<n;i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } init(1,0); for(int i=1;i<=m;i++) { scanf("%d%d",&st[i],&ed[i]); lca[i]=LCA(st[i],ed[i]); len[i]=h[st[i]]+h[ed[i]]-2*h[lca[i]]; mx=max(mx,len[i]); } l=0,r=mx; while(l<=r) { mid=(l+r)>>1; if(ck())ans=mid,r=mid-1; else l=mid+1; } printf("%d",ans); return 0; }

洛谷P2680 運輸計劃——樹上差分