Submission #3430902


Source Code Expand

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
//#define int ll

#define REP(i,n) for(int i=0;i<n;++i)
#define SORT(name) sort(name.begin(), name.end())
#define ZERO(p) memset(p, 0, sizeof(p))
#define MINUS(p) memset(p, -1, sizeof(p))
#if 1
#  define DBG(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#  define DBG(fmt, ...)
#endif

const ll LLINF = (1LL<<60);
const int INF = (1LL<<30);
const double DINF = std::numeric_limits<double>::infinity();
const int MOD = 1000000007;
#define MAX_N 2010

template <typename T, typename U>
class Dijkstra {
    private:
        struct Edge {
            T to;   // 有向辺の行先頂点
            U cost;  // 辺の重み
        };
        typedef pair<U, T> P;       // first: 最短距離 second: 頂点 id
    public:
        // コンストラクタ
        // v … 頂点数, inf: …「無限大」とする値
        Dijkstra(T v, U inf) : m_V(v), m_Inf(inf) {
            m_G.assign(m_V, vector<Edge>());
            //m_Dist.assign(m_V, m_Inf);
            m_PrevP.assign(m_V, -1);
        }
        // 有向辺の追加。負のコストは未対応
        void AddDirEdge(T from, T to, U cost) { m_G[from].push_back({to, cost}); }
        // 無向辺の追加。負のコストは未対応
        void AddUndirEdge(T from, T to, U cost) {
            AddDirEdge(from, to, cost);
            AddDirEdge(to, from, cost);
        }
        // 始点 s から各頂点への最短経路の導出 O(E log V)
        void Calc(T s) {
            m_Dist.assign(m_V, m_Inf);
            // 最短距離が短い順で cand を取り出す
            // 頂点 P.second まで P.first のコストで来れたことを情報として入れておく
            priority_queue<P, vector<P>, greater<P> > cand;
            m_Dist[s] = 0;  // 始点のコストは 0
            cand.push(P(0, s));
            while(!cand.empty()) {
                P p = cand.top(); cand.pop();     // cand のトップを取り出す
                T v = p.second;
                // 頂点 v まで最短経路が、 cand から取り出したものより既に小さいなら何もしない
                if(m_Dist[v] < p.first){ continue; }
                // 現在見ている頂点から延びている辺を全て見る
                for(Edge e : m_G[v]) {
                    if(m_Dist[e.to] > m_Dist[v] + e.cost) {
                        // 現在の最短経路より短くなるなら更新
                        m_Dist[e.to] = m_Dist[v] + e.cost;
                        m_PrevP[e.to] = v;
                        cand.push(P(m_Dist[e.to], e.to));
                    }
                }
            }
        }
        // 始点 s から頂点 t への最短距離を返す O(1)
        // 前処理として Calc(s) が必要
        U GetDist(T t) { return m_Dist[t]; }
        // 始点 s から頂点 t までの経路を vector に入れて返す (s と t 含む)
        // O(V') … V' は最短経路の頂点数
        // 前処理として Calc(s) が必要
        vector<T> GetPath(T t) {
            vector<T> path;
            for(; t != -1; t = m_PrevP[t]) { path.push_back(t); }  // t が s になるまで prev_p[t] を辿っていく
            // このままだと t->s の順になっているので逆順にする
            reverse(path.begin(), path.end());
            return path;
        }
    private:
        const T m_V;                // 頂点の数
        const U m_Inf;              // 無限の代わりとして扱う値
        vector< vector<Edge> > m_G; // 頂点 i から延びている辺の vector(隣接リスト)
        vector<U> m_Dist;           // 各頂点への最短コスト。つまり答え
        vector<T> m_PrevP;          // 最短経路の各頂点を保存(経路復元で利用)
};

int N, K;
int A[MAX_N];
int B[MAX_N];
vector<int> graph[MAX_N];   // 頂点 i から伸びている先
int dist[MAX_N][MAX_N] = {};     // 頂点 i-j 間の距離
int child[MAX_N][MAX_N] = {};    // 頂点 i から頂点 j とは逆方向の子の数

// n より下の子全追加 map を作成
void dfs(map<int, int>& m, int n, int prev) {
    m[n] = 1;
    for(auto& next_n : graph[n]) {
        if(next_n == prev) { continue; }
        dfs(m, next_n, n);
    }
}

// 距離が K/2 を超えたら自分以下の子の数を cand に加えて return
void dfs2(int& cand, int border, int root, int n, int prev) {
    if(dist[root][n] > border) {
        cand += child[n][root] + 1;
        return;
    }
    for(auto& next_n : graph[n]) {
        if(next_n == prev) { continue; }
        dfs2(cand, border, root, next_n, n);
    }
}

signed main()
{
    cin >> N >> K;
    Dijkstra<int, int> dk(N, INF);
    REP(i, N-1) {
        cin >> A[i] >> B[i];
        A[i]--; B[i]--;
        dk.AddUndirEdge(A[i], B[i], 1);
        graph[A[i]].push_back(B[i]);
        graph[B[i]].push_back(A[i]);
    }

    // dist の作成
    REP(i, N) {
        dk.Calc(i);
        REP(j, N) { dist[i][j] = dk.GetDist(j); }
    }

#if 0
    REP(i, N) {
        REP(j, N) {
            DBG("dist[%d][%d]: %d\n", i, j, dist[i][j]);
        }
    }
#endif

    // child の作成
    REP(i, N) {
        vector< map<int, int> > tree;
        REP(j, graph[i].size()) {
            map<int, int> m;
            dfs(m, graph[i][j], i);
            tree.push_back(m);
        }
        REP(j, N) {
            if(i == j) {
                child[i][j] = N - 1;
                continue;
            }
            int id = -1; // 頂点 j がいる tree のインデックス
            REP(k, tree.size()) {
                if(tree[k].count(j) != 0) {
                    id = k;
                    break;
                }
            }
            assert(id >= 0);
            REP(k, tree.size()) {
                if(k == id) { continue; }
                child[i][j] += tree[k].size();
            }
        }
    }

#if 0
    REP(i, N) {
        REP(j, N) {
            DBG("child[%d][%d]: %d\n", i, j, child[i][j]);
        }
    }
#endif

    // ある頂点 i を選ぶ(偶数の場合は 2 つ)
    // そこから K/2 でたどりつけない位置のそれ以降の子をすべて切るとき、合計の最小が答え
    int ans = N-1;
    if(K % 2 == 0) {
        REP(i, N) {
            int cand = 0;
            dfs2(cand, K/2, i, i, -1);
            ans = min(ans, cand);
        }
    } else {
        // 奇数の時は root を 2 つにする
        REP(i, N) {
            for(auto& next_root : graph[i]) {
                int cand = 0;
                dfs2(cand, (K - 1) / 2, i, i, next_root);
                dfs2(cand, (K - 1) / 2, next_root, next_root, i);
                ans = min(ans, cand);
            }
        }

    }
    printf("%d\n", ans);
    return 0;
}

Submission Info

Submission Time
Task C - Shorten Diameter
User VTR
Language C++14 (GCC 5.4.1)
Score 600
Code Size 7002 Byte
Status AC
Exec Time 1405 ms
Memory 32384 KB

Judge Result

Set Name Sample All
Score / Max Score 0 / 0 600 / 600
Status
AC × 2
AC × 34
Set Name Test Cases
Sample sample-01.txt, sample-02.txt
All 000.txt, 001.txt, 002.txt, 003.txt, 004.txt, 005.txt, 006.txt, 007.txt, 008.txt, 009.txt, 010.txt, 011.txt, 012.txt, 013.txt, 014.txt, 015.txt, 016.txt, 017.txt, 018.txt, 019.txt, 020.txt, 021.txt, 022.txt, 023.txt, 024.txt, 025.txt, 026.txt, 027.txt, 028.txt, 029.txt, 030.txt, 031.txt, sample-01.txt, sample-02.txt
Case Name Status Exec Time Memory
000.txt AC 2 ms 2304 KB
001.txt AC 1 ms 2304 KB
002.txt AC 1 ms 2304 KB
003.txt AC 1 ms 2432 KB
004.txt AC 1 ms 2432 KB
005.txt AC 1 ms 2432 KB
006.txt AC 1 ms 2432 KB
007.txt AC 1 ms 2432 KB
008.txt AC 1 ms 2432 KB
009.txt AC 1333 ms 31872 KB
010.txt AC 1305 ms 31276 KB
011.txt AC 1382 ms 31744 KB
012.txt AC 1404 ms 31872 KB
013.txt AC 1307 ms 31276 KB
014.txt AC 1394 ms 31744 KB
015.txt AC 1352 ms 31872 KB
016.txt AC 1304 ms 31276 KB
017.txt AC 1405 ms 31744 KB
018.txt AC 36 ms 9856 KB
019.txt AC 8 ms 5120 KB
020.txt AC 336 ms 18432 KB
021.txt AC 989 ms 31016 KB
022.txt AC 288 ms 17920 KB
023.txt AC 28 ms 7680 KB
024.txt AC 170 ms 14336 KB
025.txt AC 18 ms 5376 KB
026.txt AC 1155 ms 30976 KB
027.txt AC 1178 ms 32384 KB
028.txt AC 1254 ms 32384 KB
029.txt AC 1206 ms 32384 KB
030.txt AC 1361 ms 31224 KB
031.txt AC 1356 ms 31236 KB
sample-01.txt AC 1 ms 2432 KB
sample-02.txt AC 1 ms 2432 KB