题解 [NOIP2015]运输计划
题面
解析
首先肯定是要求出每条路径的长度.
这个用节点到根的前缀和就行了(一开始脑抽写了个线段树...)
然后有一个显然的类似贪心的想法,
就是你改造的边肯定在最长的路径上,
(不然没有*用)
那么考虑枚举最长的路径上的边,计算改造它的答案,
对于边\(x\),路径可以分为两类:经过它的和不经过它的.
在它被改造后,经过它的路径就都减少了它的长度,
于是最长的就还是这条最长的路径,
而没经过它的就没有受到影响,取最长的路径,
因此删掉\(x\)的答案就是上面两种情况的\(max\),
但关键就是怎么求第二种情况:没经过\(x\)的最长的路径.
枚举边再一个个求似乎不可行,
我们可以考虑枚举路径计算它对边的贡献,
显然没在这条路径上的边都可以被这条路径更新,
于是考虑树剖,
这样一条路径就被划分成了若干个区间,
而区间的补集就是它要更新的边,
把边的答案对应到深度较大的点上,用线段树维护最大值即可.
code(代码挺长但仔细康康应该能懂):
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define fot for #define ls(a) a<<1 #define rs(a) a<<1|1 #define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout) using namespace std; inline int read(){ int sum=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();} return f*sum; } const int N=3000005; struct ques{int s,t,w;}q[N]; struct edge{int to,next,w;}e[N<<1]; struct tree{int l,r,sum,maxn,tag;}t[N<<1]; struct qujian{int l,r;}sta[N]; struct node{int size,son,id,fa,dep,top,val;}a[N]; int n,m,tot,pla[N],ans; int head[N],cnt,s[N]; inline void add(int x,int y,int w){ e[++cnt]=(edge){head[x],y,w};head[x]=cnt; } inline void dfs1(int x,int fa){ a[x].size=1;a[x].fa=fa; a[x].dep=a[fa].dep+1; for(int i=head[x];i;i=e[i].to){ int k=e[i].next; if(k==fa) continue; s[k]=s[x]+e[i].w; dfs1(k,x);a[k].val=e[i].w; a[x].size+=a[k].size; if(a[k].size>a[a[x].son].size) a[x].son=k; } } inline void dfs2(int x,int top){ a[x].top=top;a[x].id=++tot; pla[tot]=x; if(a[x].son) dfs2(a[x].son,top); for(int i=head[x];i;i=e[i].to){ int k=e[i].next; if(k==a[x].fa||k==a[x].son) continue; dfs2(k,k); } } inline void build(int p,int l,int r){ t[p].l=l;t[p].r=r; if(l==r) {t[p].sum=a[pla[l]].val;return ;} int mid=(l+r)>>1; build(ls(p),l,mid);build(rs(p),mid+1,r); } inline int lca(int x,int y){ while(a[x].top!=a[y].top){ if(a[a[x].top].dep<a[a[y].top].dep) swap(x,y); x=a[a[x].top].fa; } if(a[x].dep>a[y].dep) swap(x,y); return x; } inline int dis(int x,int y){ return s[x]+s[y]-2*s[lca(x,y)]; } inline bool cmp(ques a,ques b){return a.w>b.w;} inline void Pushup(int p){ t[p].maxn=max(t[ls(p)].maxn,t[rs(p)].maxn); } inline void Pushdown(int p){ int ret=t[p].tag; t[ls(p)].tag=max(t[ls(p)].tag,ret); t[rs(p)].tag=max(t[rs(p)].tag,ret); t[ls(p)].maxn=max(t[ls(p)].maxn,ret); t[rs(p)].maxn=max(t[rs(p)].maxn,ret); } inline void change(int p,int l,int r,int w){ if(l>r||!l||!r) return ; if(t[p].l>=l&&t[p].r<=r){ t[p].maxn=max(t[p].maxn,w); t[p].tag=max(t[p].tag,w); return ; } Pushdown(p); int mid=(t[p].l+t[p].r)>>1; if(l<=mid) change(ls(p),l,r,w); if(r>mid) change(rs(p),l,r,w); Pushup(p); } inline bool cmp1(qujian a,qujian b){return a.l<b.l;} inline void find(int x,int y,int w){ int top=0; while(a[x].top!=a[y].top){ if(a[a[x].top].dep<a[a[y].top].dep) swap(x,y); sta[++top]=(qujian){a[a[x].top].id,a[x].id}; x=a[a[x].top].fa; } if(a[x].dep>a[y].dep) swap(x,y); sta[++top]=(qujian){a[x].id+1,a[y].id}; int L=1,R; sort(sta+1,sta+top+1,cmp1); for(int i=1;i<=top;i++){ R=sta[i].l-1; change(1,L,R,w); L=sta[i].r+1; } change(1,L,n,w); } inline int search(int p,int x){ if(t[p].l==t[p].r) return t[p].maxn; Pushdown(p); int mid=(t[p].l+t[p].r)>>1; if(x<=mid) return search(ls(p),x); else return search(rs(p),x); } inline void solve(int x,int y){ int pp=lca(x,y); while(x!=pp){ ans=min(ans,max(q[1].w-a[x].val,search(1,a[x].id))); x=a[x].fa; } while(y!=pp){ ans=min(ans,max(q[1].w-a[y].val,search(1,a[y].id))); y=a[y].fa; } } signed main(){ n=read();m=read(); for(int i=1;i<n;i++){ int x=read(),y=read(),w=read(); add(x,y,w);add(y,x,w); } for(int i=1;i<=m;i++){ q[i].s=read(),q[i].t=read(); } dfs1(1,0);dfs2(1,1); build(1,1,n); for(int i=1;i<=m;i++){ q[i].w=dis(q[i].s,q[i].t); } for(int i=1;i<=m;i++) find(q[i].s,q[i].t,q[i].w); sort(q+1,q+m+1,cmp); ans=q[1].w; fot(int i=1;i<=m;i++) if(q[i].w==q[1].w) solve(q[i].s,q[i].t); else break; printf("%d\n",ans); return 0; }