考虑树上乱搞
首先这是满足二分性质的,如果在某个时间可以完成工作那么比他更长的时间肯定也能完成工作
然后考虑二分,设当前答案为$mid$,如果有一条链的长度大于$mid$,那么这条链上必须得删去一条边。我们可以贪心的删去所有可以删去的边中最长的,然后看看最长边减去删去的边是否小于等于$mid$,如果成立说明可行
然后考虑怎么solve,我们可以用树上差分,就是对于每个点把它看做他父亲到他的边,每个点记录一个$s$,然后对于一条链,两个端点++,lca-=2。如果有一个点的$s$等于大于$mid$的链的条数,说明它被所有的链给覆盖,然后求一个最大值就好了
然后每一条链的lca都要求出来……实际上直接每一条都$O(logn)$求出来也没关系……不过代码里用的是tarjan离线求的,所以复杂度是$O(n+m)$
总复杂度是$O(nlogn)$(复杂度并没有降……直接树剖求LCA可能更省力啊……)
1 //minamoto 2 #include3 #include 4 #include 5 using namespace std; 6 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 7 char buf[1<<21],*p1=buf,*p2=buf; 8 template inline bool cmax(T&a,const T&b){ return a x){51 q[i].solve(),++num;52 }53 dfs(1,0);54 return mx-res<=x;55 }56 void tarjan(int u,int ff){57 for(int i=head[u];i;i=Next[i]){58 int v=ver[i];59 if(v==ff) continue;60 dis[v]=dis[u]+edge[i];61 tarjan(v,u);62 a[v]=edge[i];63 int f1=find(v),f2=find(u);64 if(f1!=f2) fa[f1]=f2;65 vis[v]=1;66 }67 for(int i=hq[u];i;i=nq[i]){68 int v=vq[i];69 if(vis[v]){70 int p=(i+1)>>1,LCA=find(v);71 q[p]=node(u,v,dis[u]+dis[v]-2*dis[LCA],LCA);72 cmax(mx,q[p].len);73 }74 }75 }76 int main(){77 // freopen("testdata.in","r",stdin);78 n=read(),m=read();79 for(int i=1,u,v,e;i >1;88 if(check(mid)) r=mid-1,ans=mid;89 else l=mid+1;90 }91 printf("%d\n",ans);92 return 0;93 }