$Noip2018/Luogu5021$ 赛道修建 二分+树形
$Sol$
一直以为是每个点只能经过一次没想到居然是每条边只能经过一次$....$
首先其实这题$55$分的部分分真的很好写啊,分别是链,数的直径和菊花图,这里就不详细说了.
使得修建的$m$条赛道中长度最小的赛道长度最大有了这句话显然就要考虑考虑二分.现在就是要考虑如何判断了.
任意选择结点作为根建树.注意到一条赛道的组成其实只有两种,一种是一条简单的由浅到深的链,另一种是由深到浅再到深这样的折了一下的链.考虑由深到浅的处理结点.对于每个结点,把子结点上传的链长加上父与子之间的边权加入$mutiset$.首先找到值已经大于等于$mid$的,直接累计答案.然后考虑内部匹配,从小到大扫,找到另一个最小的使得两者相加大于等于$mid$的值,删去这俩结点并且累计答案.最后向父结点上传剩余的链里面最长的就行.
$Code$
#include<iostream> #include<cstdio> #include<cstring> #include<set> #include<algorithm> #define il inline #define Rg register #define go(i,a,b) for(Rg int i=a;i<=b;++i) #define yes(i,a,b) for(Rg int i=a;i>=b;--i) #define e(i,u) for(Rg int i=b[u];i;i=a[i].nt) #define mem(a,b) memset(a,b,sizeof(a)) #define v(i) a[i].v #define w(i) a[i].w #define ll long long #define db double #define IT multiset<ll>::iterator #define inf 2147483647 using namespace std; il int read() { Rg int x=0,y=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();} return x*y; } const int N=50010; int n,m,b[N],ct; ll d1[N],d2[N],l=inf,r,mid,as; struct node{int v,w,nt;}a[N*2]; il void add(int u,int v,int w){a[++ct]=(node){v,w,b[u]};b[u]=ct;} il void dfs1(int u,int ft) { e(i,u) { if(v(i)==ft)continue; dfs1(v(i),u); if(d1[v(i)]+w(i)>d1[u]){d2[u]=d1[u];d1[u]=d1[v(i)]+w(i);} else d2[u]=max(d2[u],d1[v(i)]+w(i)); } } il ll dfs(int u,int fa) { multiset<ll>q;q.clear(); e(i,u) { if(v(i)==fa)continue; ll val=dfs(v(i),u)+w(i); if(val>=mid)ct++; else q.insert(val); } ll ret=0; while(q.size()) { IT i=q.begin(); IT j=q.lower_bound(mid-*i); if(((i==j)&&q.count(*i)==1))j++; if(j==q.end()){ret=max(ret,*i);q.erase(i);continue;} ct++;q.erase(q.find(*i));q.erase(q.find(*j)); } return ret; } il bool ck() { ct=0;dfs(1,0); if(ct>=m)return 1;return 0; } int main() { n=read(),m=read(); go(i,1,n-1){Rg int u=read(),v=read(),w=read();add(u,v,w);add(v,u,w);l=min(l,(ll)w);} dfs1(1,0); go(i,1,n)r=max(r,d1[i]+d2[i]); if(m==1){printf("%lld\n",r);return 0;} r+=1; while(l<=r) { mid=(l+r)>>1; if(ck())as=mid,l=mid+1; else r=mid-1; } printf("%lld\n",as); return 0; }
光伴随的阴影