村子(最大化)

肯定是越远越好哦~

那么若我们考虑一个边(edge),最多能有多少个经过?

让边的两端为a,b。最大贡献肯定是2*min(sz[a],sz[b]),sz表示子树的大小。

那怎么拿到最大贡献值呢?中心点啊啊啊!!!!!!。

若我们把中心点放为root, 那么我们可以保证最大值哦(๐॔˃̶ᗜ˂̶๐॓)。若我们在中心点开始,把左边移去右边,右边相似。

我们建村民的位置时可以用到dfs的顺序(走完左边的点先再走右边),我们利用中心点的特征:左右都有n/2的点。 所以我们可以直接放对立的。

 

具体代码:

 

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main() {
    ios_base::sync_with_stdio(0); cin.tie(0);
    int n; cin >> n;
    vector<vector<int>> g(n + 5);
    for (int i = 0; i < n - 1; i++) {
        int u, v; cin >> u >> v;
        u--; v--;
        g[u].push_back(v); g[v].push_back(u);
    }
    vector<int> sz(n + 5), a;
    int sum = 0;
    function<void(int, int)> dfs = [&](int u, int f) {
        sz[u] = 1; a.push_back(u);
        for (auto& v : g[u]) {
            if (v == f) continue;
            dfs(v, u);
            sz[u] += sz[v];
        }
        sum += 2 * min(sz[u], n - sz[u]);
    };
    function<int(int, int)>find_centroid = [&](int u, int f) {
        for (auto& v : g[u]) {
            if (v == f) continue;
            if (2 * sz[v] > n) return find_centroid(v, u);
        }
        return u;
    };
    dfs(0, -1);
    int root = find_centroid(0, -1);
    a.clear(); sum = 0;
    dfs(root, -1);
    vector<int> ans(n);
    for (int i = 0; i < n; i++) {
        ans[a[i]] = a[(i + n / 2) % n];//左右最多n/2所以肯定不会在同一个子树
    }
    cout << sum << '\n';
    for (int i = 0; i < n; i++) cout << ans[i] + 1 << ' ';
    cout << '\n';
}

 

posted on 2023-07-10 17:30  yl_neo  阅读(18)  评论(0)    收藏  举报