zoukankan      html  css  js  c++  java
  • [BZOJ1999] 树网的核 [数据加强版] (树的直径)

    传送门

    如果只是想验证算法正确性这里是洛谷数据未加强版

    Description

    设T=(V, E, W) 是一个无圈且连通的无向图(也称为无根树),每条边带有正整数的权,我们称T为树网(treenetwork),其中V, E分别表示结点与边的集合,W表示各边长度的集合,并设T有n个结点。 路径:树网中任何两结点a,b都存在唯一的一条简单路径,用d(a,b)表示以a,b为端点的路径的长度,它是该路径上各边长度之和。我们称d(a,b)为a,b两结点间的距离。 一点v到一条路径P的距离为该点与P上的最近的结点的距离: d(v,P)=min{d(v,u),u为路径P上的结点}。 树网的直径:树网中最长的路径称为树网的直径。对于给定的树网T,直径不一定是唯一的,但可以证明:各直径的中点(不一定恰好是某个结点,可能在某条边的内部)是唯一的,我们称该点为树网的中心。 偏心距ECC(F):树网T中距路径F最远的结点到路径F的距离,即 。 任务:对于给定的树网T=(V, E,W)和非负整数s,求一个路径F,它是某直径上的一段路径(该路径两端均为树网中的结点),其长度不超过s(可以等于s),使偏心距ECC(F)最小。我们称这个路径为树网T=(V,E,W)的核(Core)。必要时,F可以退化为某个结点。一般来说,在上述定义下,核不一定只有一个,但最小偏心距是唯一的。 下面的图给出了树网的一个实例。图中,A-B与A-C是两条直径,长度均为20。点W是树网的中心,EF边的长度为5。如果指定s=11,则树网的核为路径DEFG(也可以取为路径DEF),偏心距为8。如果指定s=0(或s=1、s=2),则树网的核为结点F,偏心距为12。

    Input

    包含n行: 第1行,两个正整数n和s,中间用一个空格隔开。其中n为树网结点的个数,s为树网的核的长度的上界。设结点编号依次为1, 2, ..., n。 从第2行到第n行,每行给出3个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,“2 4 7”表示连接结点2与4的边的长度为7。 所给的数据都是正确的,不必检验。

    Output

    只有一个非负整数,为指定意义下的最小偏心距。

    Sample Input

    5 2

    1 2 5

    2 3 2

    2 4 4

    2 5 3

    Sample Output

    5

    HINT

    对于70%的数据,n<=200000
    对于100%的数据:n<=500000, s<2^31, 所有权值<500

    ==============================================
    似乎SPOJ上加强版的数据...

    题解

    好题啊好题
    T了好几次,深度怀疑写的是不是O(n)算法,最后在各种改进下终于过了QAQ
    题目要求最小偏心距,我们分析题意后发现
    (最小偏心距=max(max(dis(k,ij)),dis(i,S),dis(j,T)))
    其中ij表示一段直径上的、起点为i、长度<=s的最长路径
    k为非直径上点,(max(dis(k,ij))) 表示距离ij最远的非直径点到ij的距离
    (dis(i,S))(dis(j,T))表示i和j到直径一端的距离
    (所以(dis(S,i)+dis(i,j)+dis(j,T)=dis(S,T))
    然后用单调队列记录(max(dis(k,ij)))便可以O(n)跑了

    不过我们再分析一下,发现其实不用单调队列qwq

    由于直径有最长性,任何从S,i之间分叉离开直径的子树,其最远点到i的距离一定不会比S更远(否则这就不是直径)
    这就说明 当ij中不包含点u时,(dis(k,u))一定小于(dis(i,S),dis(j,T))中的一个或两个,所以它一定不会被统计到ij的偏心距中去
    这样的话我们只需记录(max(dis(k,ST)))的值,
    如果(max(dis(k,ST)))在当前(dis(k,ij))中,那么这个值有可能比(dis(i,S),dis(j,T))大,统计到当前的偏心距中
    如果(max(dis(k,ST)))不在当前(dis(k,ij))中,那么所有dis(k,ij)都没有机会被统计,因为我们有上面的结论知道(dis(i,S),dis(j,T))中一定有一个或两个比(max(dis(k,ST)))大那么也一定大于所有(dis(k,ij))
    所以只需求出(max(dis(k,ST)))之后就和之前讲的一样,只不过不用单调队列了

    code:

    //By Menteur_Hxy
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define LL long long
    #define M(a,b) memset(a,(b),sizeof(a))
    #define F(i,a,b) for(register int i=(a);i<=(b);i++)
    #define C(i,a,b) for(register int i=(b);i>=(a);i--)
    #define E(i,u) for(register int i=head[u];i;i=nxt[i])
    #define add(a,b,c) nxt[++ecnt]=head[a],to[ecnt]=b,dis[ecnt]=c,head[a]=ecnt
    #define insert(a,b,c) add(a,b,c),add(b,a,c)
    using namespace std; 
    
    inline LL rd() {
        LL x=0,fla=1; char c=' ';
        while(c>'9'|| c<'0') {if(c=='-') fla=-fla; c=getchar();}
        while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
        return x*fla;
    }
    
    inline void out(LL x){
        int a[25],wei=0;
        if(x<0) putchar('-'),x=-x;
        for(;x;x/=10) a[++wei]=x%10;
        if(wei==0){ puts("0"); return;}
        for(int j=wei;j>=1;--j) putchar('0'+a[j]);
        putchar('
    ');
    }
    
    const int N=500010;
    int n,s,ecnt,rt,ret,len,md,r,mafa,ans=0x3f3f3f3f,S,T;
    int nxt[N<<1],dis[N<<1],to[N<<1],head[N],nd[N],dep[N],sta[N];
    int sd[N],td[N],fa[N];
    bool inr[N];
    
    inline void dfs(int u,int pre,int num) {
    	if(md<=dep[u]) rt=u,md=dep[u];
    	E(i,u) { int v=to[i];
    		if(v==pre) continue;
    		dep[v]=dep[u]+dis[i];
    		fa[v]=u;
    		dfs(v,u,num+1);
    	}
    }
    
    inline void dfs2(int u,int pre) {
    	if(dep[u]>md) md=dep[u];
    	E(i,u) { int v=to[i];
    		if(v==pre or inr[v]) continue;
    		dep[v]=dep[u]+dis[i]; 
    		dfs2(v,u);
    	}
    } 
    
    int main() {
    	n=rd(),s=rd();
    	F(i,1,n-1) {int a=rd(),b=rd(),c=rd();insert(a,b,c);}
    	dfs(1,0,1); dep[rt]=0; S=rt; fa[S]=0;
    	dfs(rt,0,1); T=rt;
    	for(int i=T;i;i=fa[i]) sd[i]=dep[i],td[i]=md-sd[i],inr[i]=1;
    	int fla=0,ret; 
    	for(int i=T;i;i=fa[i]) {
    		if(fla) ret=sd[i],sd[i]=td[i],td[i]=ret;
    		md=dep[i]=0,dfs2(i,0),mafa=max(mafa,md);
    	}
    	int t=T;
    	for(int i=T;i;i=fa[i]) { if(t==S) break;
    		while(td[fa[t]]-td[i]<=s&&fa[t]) t=fa[t]; 
    		ans=min(ans,max(mafa,max(td[i],sd[t])));
    	}
    	out(ans);
    	return 0;
    }
    
    版权声明:本文为博主原创文章,未经博主允许不得转载。 博主:https://www.cnblogs.com/Menteur-Hxy/
  • 相关阅读:
    zookeeper(四):核心原理(Watcher、事件和状态)
    zookeeper(三):java操作zookeeper
    Java并发编程(三):并发模拟(工具和Java代码介绍)
    Java并发编程(二):JAVA内存模型与同步规则
    Java并发编程(一):并发与高并发等基础概念
    zookeeper(一):功能和原理
    Sql 获取向上取整、向下取整、四舍五入取整的实例(转)
    gRPC详解
    Google Protobuf简明教程
    nginx error_page配置
  • 原文地址:https://www.cnblogs.com/Menteur-Hxy/p/9279465.html
Copyright © 2011-2022 走看看