[NOIP 2005] 运输计划
这是一道假的图论
思维难度很低,代码量偏高
就是一道板子+二分
树上差分就AC了
注意卡常即可
二分枚举答案x,为时间长度
将每一个长度大于x的计划链长记录下来(有几个,总需要减少多少长度)
在树上跑一跑即可
树上差分就将u,vv权值+1,lca(u,v)-2即可
dp[i]:为子树权值和,也是i上面道路有几条路径覆盖
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; inline int read() { int f=1,ans=0;char c; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } int n,m; struct node{ int u,v,w,nex; }x[600001]; int deep[300001],vt[300001],dis[300001],fa[300001][21],head[300001],cnt=1; void add(int u,int v,int w) { x[cnt].u=u,x[cnt].v=v,x[cnt].w=w,x[cnt].nex=head[u],head[u]=cnt++; } void dfs(int f,int fath,int DIS) { deep[f]=deep[fath]+1; dis[f]=DIS; fa[f][0]=fath; for(int i=1;(1<<i)<=deep[f];i++) fa[f][i]=fa[fa[f][i-1]][i-1]; for(int i=head[f];i;i=x[i].nex) { if(x[i].v==fath) continue; vt[x[i].v]=x[i].w; dfs(x[i].v,f,DIS+x[i].w); } return; } int lca(int u,int v) { if(deep[u]<deep[v]) swap(u,v); for(int i=19;i>=0;i--) if(deep[u]-(1<<i)>=deep[v]) u=fa[u][i]; if(u==v) return u; for(int i=19;i>=0;i--) { if(fa[u][i]==fa[v][i]) continue; u=fa[u][i],v=fa[v][i]; }return fa[u][0]; } struct node1{ int x,y,lca,dis; }s[300001]; int l,r,mid,dp[300001]; void dp_tree(int f,int fath) { for(int i=head[f];i;i=x[i].nex) { if(x[i].v==fath) continue; dp_tree(x[i].v,f); dp[f]+=dp[x[i].v]; } return; } bool check(int st) { memset(dp,0,sizeof(dp)); int tot=0,maxn=0; for(int i=1;i<=m;i++) { if(s[i].dis>st) { tot++; dp[s[i].x]++,dp[s[i].y]++,dp[s[i].lca]-=2; maxn=max(maxn,s[i].dis-st); } } dp_tree(1,0); for(int i=2;i<=n;i++) if(dp[i]==tot&&vt[i]>=maxn) return 1; return 0; } int data=2<<30-1; int main() { n=read(),m=read(); for(int i=1;i<n;i++) { int u=read(),v=read(),w=read(); add(u,v,w),add(v,u,w); } dfs(1,0,0); for(int i=1;i<=m;i++) { s[i].x=read(),s[i].y=read(); s[i].lca=lca(s[i].x,s[i].y); s[i].dis=dis[s[i].x]+dis[s[i].y]-(dis[s[i].lca]<<1); r=max(r,s[i].dis); } while(l<=r) { mid=l+r>>1; if(check(mid)) data=min(data,mid),r=mid-1; else l=mid+1; } printf("%d",data); }