1. 程式人生 > >【模擬賽 2018-11-02】3.老大

【模擬賽 2018-11-02】3.老大

題目描述

因為 OB 今年拿下 4 塊金牌,學校贊助擴建勞模辦公室為勞模辦公室群,為了體現 OI 的特色,辦公室群被設計成了樹形(n 個點 n − 1 條邊的無向連通圖),由於新建的辦公室太大以至於要將獎盃要分放在兩個不同的地方以便同學們丟硬幣進去開光,OB 想請你幫幫他看看獎盃放在哪兩個辦公室使得在任意一個在勞模辦公室做題的小朋友能最快地找到獎盃來開光。
一句話題意:給出一個 n 個點的樹,在兩個合適且不同的點放上獎盃,使得每個點到最近的獎盃距離最大值最小。

輸入

第一行,一個整數 n。
接下來的 n − 1 行,每行兩個數 x y

輸出

一個數,表示最小的最大距離。

樣例輸入

8
1 2
1 3
2 4
2 5
3 6
3 7
1 8

樣例輸出

2

提示

對於前 60% 的資料,n ≤ 100。
對於前 80% 的資料,n ≤ 2000。
對於 80% 的資料,保證樹的形態隨機。
對於 100% 的資料,保證 3 ≤ n ≤ 200000。

這道題有好多種解法,把題解貼一貼~

3.1 60% O(n3)

n2 列舉兩個獎盃位置,再 O(n) 掃一遍看看每個位置離最近獎盃最遠是多少。

3.2 80% O(n2)

考慮兩個獎盃管轄的區域必定有一個邊界,我們列舉這個邊界,也就是一條邊,其中一部
分是子樹,一部分是子樹外,我們只要分別求出另外兩個樹的直徑。
3.3 樹形態隨機

期望樹的直徑很短,兩個獎盃都在直徑上列舉。
3.4 100% 二分答案 1 O(nlogn)

獎盃在直徑上,二分答案後取離直徑上離端點距離答案的點,遍歷 check 一遍。
3.5 100% 二分答案 2 O(nlogn)

隨便提一個節點為根,二分答案,深度最深的節點一定要被照顧到,所以最深的點往上跳
答案層即可,和其距離答案以內的點都刪掉,再做一次。(我最開始的做法QAQ)
此法可以拓展到 k 個獎盃,由皮皮軒友情提供。

3.6 100% 樹形 dp O(n)

80 分的基礎上用樹形 dp,記下每個點向下前三長和向上一格後不回該子樹最長的路徑
長度。子樹內直徑是前兩長的和與該子樹各個子樹直徑取 max;子樹外直徑是父節點向上一格
後不回該子樹最長的路徑長度,前兩長不進入該子樹的向下最長路徑這三條取前兩長加起來與
父節點以上的答案取 max。 

 


 

我使用的應該算第二種二分。

也就是先二分答案——最短距離x,再按深度從大到小列舉節點,如果發現有一個節點距最近獎盃的距離大於x,即!v[i],那麼找它的父親的父親的父親。。。的父親(共x個父親)t,在t節點放置一個獎盃,然後搜尋將和它距離小於等於x的節點,標記v[i]等於1。

如果發現v[i] == 0 && cnt >= 2 就直接返回0,因為已經沒有更多的獎盃可以放了。這種貪心策略很容易證明,這裡不再贅述。

我的AC之路多麼坎坷——

1.用了DFS,自己生成一個200000個節點的鍊形樹,結果發現RE,懷疑可能爆棧,我改。x

2.用了BFS,然後隨機生成一棵200000個節點的樹,雖然過了,但是發現時間有點慢,用了clock()輸出所用時間,發現用了1000+ms,可能會TLE,我再改。

3.輸出 讀入所用時間,發現已經是幾百ms,毅然將scanf改為讀優,用了一些register,降了一些複雜度,但仍然不能滿足要求,我還要改。

4.懷疑BFS時直接用了queue,複雜度會倍增,於是手打佇列,發現複雜度一下子減掉了一半,只有400~500+ms水平,估計沒有問題,但是仍然打了對拍(其實DFS打好以後就打了對拍),沒有發現錯誤,實際上也AC了。

給出了那些我打的程式碼QAQ

#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005

int n, x, y;
int head[MAXN], nxt[MAXN << 1], to[MAXN << 1], tot;
int dis[MAXN];

inline void Add( int x, int y ){
    to[++tot] = y; nxt[tot] = head[x]; head[x] = tot;
}

void dfs( int x, int fa, int d ){
    dis[x] = min( dis[x], d );
    for ( int i = head[x]; i; i = nxt[i] ) if ( to[i] != fa ) dfs( to[i], x, d + 1 );
}

int main(){
    scanf( "%d", &n );
    for ( int i = 1; i < n; ++i ){
        scanf( "%d%d", &x, &y ); Add( x, y ); Add( y, x );
    }
    int ans(0x7f7f7f7f);
    for ( int i = 1; i <= n; ++i )
        for ( int j = i + 1; j <= n; ++j ){
            memset( dis, 0x3f, sizeof dis );
            dfs( i, i, 0 );
            dfs( j, j, 0 );
            int cur(0);
            for ( int k = 1; k <= n; ++k ) cur = max( cur, dis[k] );
            ans = min( ans, cur );
        }
    printf( "%d\n", ans );
    return 0;
}
ob-baoli.cpp
#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005

int n, x, y;
int head[MAXN], nxt[MAXN << 1], to[MAXN << 1], tot;
int dep[MAXN], o[MAXN], f[MAXN];
int v[MAXN];

bool cmp( int x, int y ){
    return dep[x] > dep[y];
}

inline void Add( int x, int y ){
    to[++tot] = y; nxt[tot] = head[x]; head[x] = tot;
}

void DFS( int x, int fa, int d ){
    dep[x] = d; f[x] = fa;
    for ( int i = head[x]; i; i = nxt[i] )
        if ( to[i] != fa ) DFS( to[i], x, d + 1 );
}

void dfs( int x, int fa, int d, int maxd ){
    if ( d > maxd ) return;
    v[x] = 1;
    for ( int i = head[x]; i; i = nxt[i] )
        if ( to[i] != fa ) dfs( to[i], x, d + 1, maxd );
}

bool check( int x ){
    int cur(0); memset( v, 0, sizeof v );
    for ( int i = 1; i <= n; ++i ){
        if ( !v[o[i]] ){
            int t(o[i]);
            for ( int j = 1; j <= x; ++j ) t = f[t];
            cur++;
            if ( cur > 2 ) return 0;
            dfs( t, t, 0, x ); 
        }
    }
    return 1;
}

int main(){
    scanf( "%d", &n );
    for ( int i = 1; i < n; ++i ){
        scanf( "%d%d", &x, &y ); Add( x, y ); Add( y, x );
    }
    DFS( 1, 1, 1 );
    for ( int i = 1; i <= n; ++i ) o[i] = i;
    sort( o + 1, o + n + 1, cmp );
    int l(1), r(200000), mid, ans(-1);
    while( l <= r ){
        mid = ( l + r ) >> 1;
        if ( check( mid ) ) r = mid - 1, ans = mid;
        else l = mid + 1;
    }
    printf( "%d\n", ans );
    return 0;
}
ob-dfs.cpp
#include<bits/stdc++.h>
using namespace std;
#define MAXN 400005

int n, x, y;
int head[MAXN], nxt[MAXN << 1], to[MAXN << 1], tot;
int dep[MAXN], o[MAXN], f[MAXN];
int v[MAXN];

bool cmp( int x, int y ){
    return dep[x] > dep[y];
}

inline void Add( int x, int y ){
    to[++tot] = y; nxt[tot] = head[x]; head[x] = tot;
}

void BFS(){
    queue<int> Q;
    Q.push(1); f[1] = 1; dep[1] = 1;
    
    while( !Q.empty() ){
        int t(Q.front()); Q.pop();
        for ( int i = head[t]; i; i = nxt[i] )
            if ( to[i] != f[t] ){
                dep[to[i]] = dep[t] + 1; f[to[i]] = t; Q.push(to[i]);
            }
    }
}

struct states{
    int id, de, fa;
};

states make_st( int x, int y, int z ){
    states t;
    t.id = x; t.de = y; t.fa = z;
    return t;
}

void bfs( int x, int maxd ){
    queue<states> Q;
    Q.push( make_st( x, 0, x ) );
    while( !Q.empty() ){
        int t(Q.front().id), d(Q.front().de), fth(Q.front().fa);
        Q.pop(); v[t] = 1;
        if ( d >= maxd ) continue;
        for ( int i = head[t]; i; i = nxt[i] )
            if ( to[i] != fth ) Q.push( make_st( to[i], d + 1, t ) );
    }
}

bool check( int x ){
    int cur(0); memset( v, 0, sizeof v );
    for ( int i = 1; i <= n; ++i ){
        if ( !v[o[i]] ){
            int t(o[i]);
            for ( int j = 1; j <= x; ++j ) t = f[t];
            cur++;
            if ( cur > 2 ) return 0;
            bfs( t, x ); 
        }
    }
    return 1;
}

int main(){
    scanf( "%d", &n );
    for ( int i = 1; i < n; ++i ){
        scanf( "%d%d", &x, &y ); Add( x, y ); Add( y, x );
    }
    BFS();
    for ( int i = 1; i <= n; ++i ) o[i] = i;
    sort( o + 1, o + n + 1, cmp );
    int l(1), r(200000), mid, ans(-1);
    while( l <= r ){
        mid = ( l + r ) >> 1;
        if ( check( mid ) ) r = mid - 1, ans = mid;
        else l = mid + 1;
    }
    printf( "%d\n", ans );
    return 0;
}
ob-bfs.cpp ob-bfs-no stl.cpp

樹形DP我還沒打過,只能等以後再來填坑了。

祝大家AC愉快QAQ。