bzoj1999 [Noip2007]Core树网的核(树的直径+单调队列+贪心)

132 篇文章 0 订阅

加强版,n变成了500000.本来的瞎搞法就gg了
怎么办呢?我们需要更高明的瞎搞(逃
首先我们知道两条性质:
(1)对于树中的任意一点,距离其最远的点一定是树的直径的某一端点。

(2)所有的直径是等价的,即任意一条直径所能求出的最小偏心距相等。

于是我们两遍dfs先求出任意一条直径,并记录下来。堵上直径上的所有点,从直径上的每一个点出发遍历他能到达的点,求出不经过直径距它最远的点的距离,记作mxd。如果我们选取直径上的一段(l,r),则它的偏心距就是
max(l到直径左端点的距离,r到直径右端点的距离,max(mxd[l~r])).
于是我们枚举左端点l,显然r在不超过s的限制下越远越好,于是我们可以找到他的右端点r,然后计算偏心距更新答案即可。再用一个单调队列优化一下。
就是 O(n) 的了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 500010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,s,fa[N],a[N],m,h[N],num=0,mx,rt,q[N];//a--直径
ll dis[N],mxd[N],ans=1LL<<60;bool mark[N];
struct edge{
    int to,next,val;
}data[N<<1];
inline void dfs(int x){
    if(dis[x]>dis[mx]) mx=x; 
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==fa[x]) continue;
        fa[y]=x;dis[y]=dis[x]+data[i].val;dfs(y);
    }
}
inline void dfs2(int x){
    if(mxd[x]>mxd[mx]) mx=x;
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==fa[x]||mark[y]) continue;
        mxd[y]=mxd[x]+data[i].val;dfs2(y);
    }
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();s=read();
    for(int i=1;i<n;++i){
        int x=read(),y=read(),val=read();
        data[++num].to=y;data[num].next=h[x];h[x]=num;data[num].val=val;
        data[++num].to=x;data[num].next=h[y];h[y]=num;data[num].val=val;
    }mx=1;dfs(1);rt=mx;fa[rt]=0;dis[rt]=0;dfs(rt);
    while(mx) a[++m]=mx,mark[mx]=1,mx=fa[mx];
    for(int i=1;i<=m;++i){
        mx=a[i];dfs2(a[i]);mxd[a[i]]=mxd[mx];
    }int l=1,r=1,h=1,t=0;
    for(;l<=m;++l){
        while(h<=t&&q[h]<l) ++h;
        while(r<=m&&dis[a[l]]-dis[a[r]]<=s){
            while(h<=t&&mxd[a[r]]>=mxd[a[q[t]]]) --t; 
            q[++t]=r;++r;
        }ans=min(ans,max(max(dis[a[1]]-dis[a[l]],dis[a[r-1]]),mxd[a[q[h]]]));
    }printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值