• 「CTSC2017」网络


    题目

    点这里看题目。

    分析

    可以说,这是一道思路比较常规的题目,但是某些看待问题的角度还是可以学习的。

    首先从题目中注意到两个关键信息:

    • 最长最短路——直径——联系到原树的直径;
    • 显然答案是可以二分的;

    原树的直径这个东西怎么用?我们不妨先把原树的直径提作“根”。下面是一个显然的结论:

    新加边的一个端点一定落在了直径上。

    这个东西太弱了,我们进行加强就可以想到:

    新加边的两个端点一定都落在直径上

    第一个很好证明,我们考虑第二个:

    先将直径 \((u,v)\) 作为根,我们假设新加边的一个端点 \(x\) 在直径上,另一个 \(y\) 不在(\(y\) 更靠近 \(v\));不在直径上的端点对应直径上的根为 \(w\)

    网络.png

    尝试说明 \(y\) 向根走一步之后答案更优:先将环提作根,而后只考虑 \(w\) 的子树;当 \(w\) 子树内任意一个点作为根时,离它最远的点之一一定是 \(v\)。所以当 \(y\)\(w\) 走一步之后,\(v\)\(y\) 的距离一定会变小,才有可能使答案更优。而其它结点到 \(y\) 的贡献实际上不需要考虑。

    所以,设直径为 \(l\)。我们将直径 \((u,v)\) 上的点拉成一行,从 \(u\)\(v\) 按照 \(1,2,\dots,l+1\) 编号。设 \(s_{i}\) 为第 \(i\) 个点到 \(u\) 的距离,\(f_i\) 为第 \(i\) 个点的以直径为根的子树内的最远点距离;假设新加边为 \((a,b),a<b\)。这样,我们可以数量化新加边之后,经过原直径上至少一条边的新直径:

    \[\max_{1\le i<j\le l}\{\min\{f_i+f_j+s_j-s_i,f_i+f_j+|s_a-s_i|+|s_b-s_j|+L\}\} \]

    当我们二分好答案为 \(D\) 之后,我们就是要求,对于所有 \(i<j\),都有:

    \[\min\{f_i+f_j+s_j-s_i,f_i+f_j+|s_a-s_i|+|s_b-s_j|+L\}\le D \]

    \(\min\) 的组合含义是存在任一。注意到前面一项和如何选择 \(a,b\) 没有关系,因此不满足前一项的一定要满足后一项。这个绝对值比较尴尬,但我们知道绝对值可以变成 \(\max\),因此最终我们还是得到了四组 \(\le D\) 且需要同时成立的限制。

    整理一下,我们要求 \(i<j\)\(f_i+f_j+s_j-s_i>D\),这可以被转化为二维偏序问题,使用树状数组维护若干个最值,最终得到如下限制:

    \[\begin{cases} l_+\le s_a+s_b\le r_+\\ l_-\le s_a-s_b\le r_- \end{cases} \]

    之后就容易 \(O(n)\) 处理了;这样总的复杂度为 \(O(n\log n\log l)\)

    小结:

    1. 一些必要的联想:与最长最短路相关的,一般都可以想到直径和二分
    2. 这个证明思路可以学习一下,不过实际上很好猜;
    3. 数量化是一个很重要的步骤。形式化地描述某些东西总不是坏事,甚至我们可以看清它的结构
    4. 关注 \(\min,\max\) 在不等式中的组合含义,以及处理 \(|a|\) 的方法。

    代码

    #include <set>
    #include <cstdio>
    #include <algorithm>
    
    #define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
    #define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
    
    typedef long long LL;
    
    const LL INF = 4e18;
    const int MAXN = 1e5 + 5;
    
    template<typename _T>
    void read( _T &x ) {
        x = 0; char s = getchar(); bool f = false;
        while( s < '0' || '9' < s ) { f = s == '-', s = getchar(); }
        while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
        if( f ) x = -x;
    }
    
    template<typename _T>
    void write( _T x ) {
        if( x < 0 ) putchar( '-' ), x = -x;
        if( 9 < x ) write( x / 10 );
        putchar( x % 10 + '0' );
    }
    
    template<typename _T>
    _T MIN( const _T a, const _T b ) {
        return a < b ? a : b;
    }
    
    template<typename _T>
    _T MAX( const _T a, const _T b ) {
        return a > b ? a : b;
    }
    
    struct Edge {
        int to, nxt; LL w;
    } Graph[MAXN << 1];
    
    inline void Down( int &x ) { x &= x - 1; }
    inline void Up( int &x ) { x += x & ( - x ); }
    
    struct MinBIT {
        LL val[MAXN]; int n;
    
        MinBIT(): val{}, n( 0 ) {}
    
        void Init( const int N ) { n = N; rep( i, 1, n ) val[i] = INF; }
        void Update( int x, const LL v ) { for( ; x <= n ; Up( x ) ) val[x] = MIN( val[x], v ); }
        LL   Query( int x ) { LL ret = INF; for( ; x ; Down( x ) ) ret = MIN( ret, val[x] ); return ret; }
    };
    
    struct MaxBIT {
        LL val[MAXN]; int n;
    
        MaxBIT(): val{}, n( 0 ) {}
    
        void Init( const int N ) { n = N; rep( i, 1, n ) val[i] = - INF; }
        void Update( int x, const LL v ) { for( ; x <= n ; Up( x ) ) val[x] = MAX( val[x], v ); }
        LL   Query( int x ) { LL ret = - INF; for( ; x ; Down( x ) ) ret = MAX( ret, val[x] ); return ret; }
    };
    
    MinBIT mn;
    MaxBIT mx;
    
    std :: set<LL> s;
    
    LL sufMx[MAXN], sufMn[MAXN];
    int seq1[MAXN], seq2[MAXN];
    
    LL mst[MAXN], far[MAXN], pref[MAXN];
    LL dist[MAXN];
    
    int pat[MAXN], tot;
    bool onPat[MAXN];
    
    int head[MAXN], cnt = 1;
    
    LL len, adUp, adDn, sbUp, sbDn;
    int N;
    
    void AddEdge( const int from, const int to, const LL W ) {
        Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
        Graph[cnt].w = W, head[from] = cnt;
    }
    
    int BFS( const int S ) {
        static int q[MAXN] = {};
        int h = 1, t = 0;
        rep( i, 1, N ) dist[i] = INF;
        dist[q[++ t] = S] = 0;
        while( h <= t ) {
            int u = q[h ++];
            for( int i = head[u], v ; i ; i = Graph[i].nxt )
                if( dist[v = Graph[i].to] > dist[u] + Graph[i].w )
                    dist[q[++ t] = v] = dist[u] + Graph[i].w;
        }
        int mx = 1;
        rep( i, 2, N )
            if( dist[mx] < dist[i] )
                mx = i;
        return mx;
    }
    
    bool GetPath( const int u, const int fa, const int tar ) {
        bool ret = false;
        if( u == tar ) ret = true;
        else
            for( int i = head[u], v ; i ; i = Graph[i].nxt )
                if( ( v = Graph[i].to ) ^ fa )
                    ret |= GetPath( v, u, tar );
        if( ret ) pat[++ tot] = u;
        return ret;
    }
    
    LL Init( const int u, const int fa ) {
        LL ret = 0, mx = 0, sm = - INF, tmp;
        for( int i = head[u], v ; i ; i = Graph[i].nxt )
            if( ( v = Graph[i].to ) ^ fa && ! onPat[v] ) {
                ret = MAX( ret, Init( v, u ) ); 
                tmp = mst[v] + Graph[i].w;
                if( mx <= tmp ) sm = mx, mx = tmp;
                else if( sm < tmp ) sm = tmp;
            }
        mst[u] = mx;
        return MAX( ret, sm + mx );
    }
    
    bool Chk( const LL lim ) {
        adUp = sbUp = INF, adDn = sbDn = - INF;
        rep( i, 1, tot ) seq1[i] = seq2[i] = i;
        std :: sort( seq1 + 1, seq1 + 1 + tot, 
            [] ( const int &a, const int &b ) -> bool {
                return far[a] - pref[a] < far[b] - pref[b];
            } );
        std :: sort( seq2 + 1, seq2 + 1 + tot,
            [] ( const int &a, const int &b ) -> bool {
                return far[a] + pref[a] < far[b] + pref[b];
            } );
        mn.Init( tot ), mx.Init( tot );
        for( int i = tot, j = 1 ; j <= tot ; j ++ ) {
            int u = seq2[j];
            while( i > 1 && ( far[seq1[i]] - pref[seq1[i]] ) + ( far[u] + pref[u] ) > lim ) 
                mn.Update( seq1[i], pref[seq1[i]] - far[seq1[i]] ),
                mx.Update( seq1[i], pref[seq1[i]] + far[seq1[i]] ), i --;
            adUp = MIN( adUp, lim - len + pref[u] - far[u] + mn.Query( u - 1 ) );
            sbUp = MIN( sbUp, lim - len - pref[u] - far[u] + mn.Query( u - 1 ) );
            sbDn = MAX( sbDn, - lim + len - pref[u] + far[u] + mx.Query( u - 1 ) );
            adDn = MAX( adDn, - lim + len + pref[u] + far[u] + mx.Query( u - 1 ) );
        }
        int l1 = tot + 1, r1 = tot, l2 = 1, r2 = 0;
        rep( i, 1, tot ) {
            while( l1 > 1 && pref[l1 - 1] >= adDn - pref[i] ) l1 --;
            while( r1 >= 1 && pref[r1] > adUp - pref[i] ) r1 --;
            while( l2 <= tot && pref[l2] < sbDn + pref[i] ) l2 ++;
            while( r2 < tot && pref[r2 + 1] <= sbUp + pref[i] ) r2 ++;
            if( MAX( l1, l2 ) <= MIN( r1, r2 ) ) return true;
        }
        return false;
    }
    
    int main() {
        while( true ) {
            cnt = 1;
            read( N ), read( len );
            if( ! N && ! len ) break;
            rep( i, 1, N ) head[i] = 0, onPat[i] = false;
            rep( i, 1, N - 1 ) {
                int u, v; LL w;
                read( u ), read( v ), read( w );
                AddEdge( u, v, w ), AddEdge( v, u, w ); 
            }
            int beg = BFS( 1 ), ed = BFS( beg );
            tot = 0, GetPath( beg, 0, ed );
            std :: reverse( pat + 1, pat + 1 + tot );
            pref[1] = 0;
            LL l = 0, r = dist[ed], mid;
            rep( i, 1, tot ) onPat[pat[i]] = true;
            rep( i, 1, tot ) {
                l = MAX( l, Init( pat[i], 0 ) );
                far[i] = mst[pat[i]];
                if( i == tot ) continue;
                for( int j = head[pat[i]] ; j ; j = Graph[j].nxt )
                    if( Graph[j].to == pat[i + 1] )
                        pref[i + 1] = pref[i] + Graph[j].w;
            }
            while( l < r ) {
                mid = ( l + r ) >> 1;
                if( Chk( mid ) ) r = mid;
                else l = mid + 1;
            }
            write( l ), putchar( '\n' );
        }
        return 0;
    }
    
  • 相关阅读:
    GPU CUDA之——深入理解threadIdx
    需求分析、业务逻辑与数据结构
    软件建模的本质
    浅谈软件需求建模
    软件建模即程序设计
    软件开发从0到1与软件建模
    数据模型所描述的内容包括三个部分:数据结构、数据操作、数据约束。
    观察力与信息搜集能力
    人类为什么写书
    鲁宾斯坦说:"思维是在概括中完成的。"
  • 原文地址:https://www.cnblogs.com/crashed/p/15943777.html
Copyright © 2020-2023  润新知