bzoj 1999

9 篇文章 0 订阅

树网的核 加强版,好题~


可以证明,如果存在多条直径,选取任意一条直径都是等价的。


证明:

如图,考虑两条直径 AB, CD,其中它们的重合部分为 EF

 A                           B
  \                         /                         
   \                       /
    \_____________________/
    /E                   F\
   /                       \
  /                         \
 C                           D

先证 |AE| = |CE|, |BF| = |DF|

|AE| +|EF| + |BF| = |CE| + |EF| + |DF| |AE| + |BF| = |CE| + |DF|

若|AE| > |CE|, 则 |BF| < |DF|,那么 |AD| >|AB|
而AB为树的直径,推出矛盾,所以 |AE| <= |CE|

同理可证 |AE| >= |CE|,|BF| <= |DF|,|BF| >= |DF|

|AE| = |CE|, |BF| = |DF|


现在证明,在任意一条直径上选取路径 p 计算偏心距是等价的。

如果 p 没有覆盖 EF,其它直径上的点对偏心距没有贡献。

如果 p 覆盖 EF。

  • p 在AB上时,CD上的点对偏心距的贡献为 |CE| 和 |DF|

    • p 在CD上时,AB上的点对偏心距的贡献为 |AE| 和 |BF|

    |AE| = |CE|, |BF| = |DF|
    在任意一条直径上选取路径 p 计算偏心距是等价的

    综上所述,结论成立。


    根据贪心的原则,在直径上选取尽量长的路径 p 可以使得偏心距最小。

    对于这条路径,如何计算偏心距?

    记直径左端点为 L, 右端点为 R,这条路径左端点为 l ,右端点为 r

    ECC(p) = max { max { g(i) | i p }, dist(L , l) , dist(r , R) }

    其中 g(i) 表示 i 点不经过直径能到达的最远距离。

    对于 max{ g(i) | i p },用单调队列维护 g(i) | i p 下降即可。

    时间复杂度: O(n)


    /**************************************************************
        Problem: 1999
        User: cyxhahaha
        Language: C++
        Result: Accepted
        Time:1496 ms
        Memory:58324 kb
    ****************************************************************/
    
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <ctime>
    #include <string>
    #include <map>
    #include <vector>
    #include <stack>
    #include <queue>
    #include <utility>
    #include <iostream>
    #include <algorithm>
    
    template<class Num>void read(Num &x)
    {
        char c; int flag = 1;
        while((c = getchar()) < '0' || c > '9')
            if(c == '-') flag *= -1;
        x = c - '0';
        while((c = getchar()) >= '0' && c <= '9')
            x = (x<<3) + (x<<1) + (c-'0');
        x *= flag;
        return;
    }
    template<class Num>void write(Num x)
    {
        if(x < 0) putchar('-'), x = -x;
        static char s[20];int sl = 0;
        while(x) s[sl++] = x%10 + '0',x /= 10;
        if(!sl) {putchar('0');return;}
        while(sl) putchar(s[--sl]);
    }
    
    #define REP(__x,__st,__ed) for(int __x = (__st); __x <= (__ed); __x++)
    
    const int maxn = 5e5 + 5, Nya = -1;
    
    int n, S;
    
    struct Edge
    {
        int v, w, next;
    
        Edge(int v=0,int w=0,int next=0):v(v),w(w),next(next){}
    
    }edge[maxn<<1];
    
    int el, head[maxn];
    
    int dist[maxn], path[maxn], len;
    int Lmax[maxn], Rmax[maxn], Mmax[maxn];
    bool hash[maxn];
    
    void NewEdge(int u,int v,int w)
    {
        edge[++el] = Edge(v, w, head[u]), head[u] = el;
    }
    
    void dfs(int a,int fa,int arr[])
    {
        for(int i = head[a]; i; i = edge[i].next)
        {
            int p = edge[i].v;
    
            if(p == fa) continue;
    
            arr[p] = arr[a] + edge[i].w;
    
            dfs(p, a, arr);
        }
    }
    
    int gainmax(int a,int fa)
    {
        int ret = 0;
    
        for(int i = head[a]; i ; i = edge[i].next)
        {
            int p = edge[i].v;
    
            if(p == fa || hash[p]) continue;
    
            ret = std::max(gainmax(p, a) + edge[i].w, ret);
        }
        return ret;
    }
    
    bool find(int a,int aim,int fa)
    {
        if(a == aim)
        {
            path[++len] = a;
            return true;
        }
        for(int i = head[a]; i; i = edge[i].next)
        {
            if(edge[i].v == fa) continue;
    
            if(find(edge[i].v, aim, a))
            {
                path[++len] = a;
                return true;
            }
        }
        return false;
    }
    void init()
    {
        int u, v, w;
    
        read(n), read(S);
        for(int i = 1; i < n; i++)
        {
            read(u), read(v), read(w);
            NewEdge(u, v, w);
            NewEdge(v, u, w);
        }
    }
    void diameter()
    {
        int s = 1, t = 1;
    
        dist[1] = 0, dfs(1, 0, dist);
    
        REP(i, 1, n)
            if(dist[i] > dist[s])
                s = i;
    
        dist[s] = 0, dfs(s, 0, dist);
    
        REP(i, 1, n)
            if(dist[i] > dist[t])
                t = i;
    
        find(t, s, 0);              
    
        REP(i, 1, len) hash[path[i]] = true;
    
        REP(i, 1, len) Mmax[i] = gainmax(path[i], 0);
    
        REP(i, 1, len)
        {
            Lmax[i] = dist[path[i]] - dist[path[1]];
            Rmax[i] = dist[path[len]] - dist[path[i]];
        }
    }
    void solve()
    {
        static int line[maxn];
        int f = 0, r = 0, ans = Nya, cal;
    
        for(int i = 1, j = 1; i <= len; i++)
        {
            if(f != r && line[f] < i) line[f++] = 0;
    
            while(j <= n && dist[path[j]] - dist[path[i]] <= S)
            {
                while(f != r && Mmax[line[r-1]] <= Mmax[j]) line[--r] = 0;
                line[r++] = j++;
            }
    
            cal = std::max(Mmax[line[f]], std::max(Lmax[i], Rmax[j-1]));
    
            if(ans == Nya || cal < ans) ans = cal;
    
        }
    
        write(ans);
    }
    
    int main()
    {
    #ifndef ONLINE_JUDGE    
        freopen("1999.in","r",stdin);
        freopen("1999.out","w",stdout);
    #endif
    
        init(), diameter(), solve();
    
    #ifndef ONLINE_JUDGE    
        fclose(stdin);
        fclose(stdout);
    #endif
        return 0;   
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值