JZOJ1497 景点中心 题解

题目:

数据范围以及样例解释:

思路

对于30%的数据,爱怎么做怎么做,我就不信你拿不到。
对于60%的数据,枚举所有的点作为根,以该点根做dfs可以求出每个点到根的距离dis[i],用每个点的学生人数dis[i]再加起来就是对应的答案,取个最小值即可。枚举O(n),dfs复杂度也是O(n),因此复杂度为O(n^2)。只能通过60%数据。
对于100%的数据,考虑换根法。即先随意定一点为根(也就是景点中心),并求出其对应答案。我们在60%的做法中,每枚举一个点都对其重新求一遍答案,这样子的效率极其低下,因为我们可以根据已有答案O(1)转移。若已知当前根为u的答案,我们将根变成u的儿子v,那么u的其他儿子的子树中的每个点,都要多走u到v的这段长度,而v的子树中的每个点,都会少走u到v的这段长度。于是我们可以先假设1为根,然后暴力求出其答案,同时求出sum[i]表示以1为根的情况下i的子树中的学生总数。设原来的答案是ret,换成v为根后答案就是ret+(sum[1]-sum[v])
len[i]-sum[v]*len[i]。
复杂度就是O(n)
代码:

#include <cstdio>
#include <cstring>
#include <cstdlib>
inline int read()
{
    int x = 0, f = 0;
    char c = getchar();
    for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = 1;
    for (; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ '0');
    return f ? -x : x;
}

typedef long long ll;
const int N = 1e5 + 7;

int tot = 0, st[N], to[N << 1], nx[N << 1];
ll sum[N], a[N], len[N << 1], mi = 0;
int n, ans;

inline void add(int u, int v, int w) { to[++tot] = v, nx[tot] = st[u], len[tot] = (ll)w, st[u] = tot; }
void dfs(int u, int from)
{
    sum[u] = a[u];
    for (int i = st[u]; i; i = nx[i]) if (to[i] != from) dfs(to[i], u), sum[u] += sum[to[i]], mi += sum[to[i]] * len[i];
}
void getans(int u, int from, ll ret)
{
    if (ret < mi) mi = ret, ans = u;
    for (int i = st[u]; i; i = nx[i])
        if (to[i] != from)
            getans(to[i], u, ret - sum[to[i]] * len[i] + (sum[1] - sum[to[i]]) * len[i]);
}

int main()
{
    n = read();
    for (int i = 1; i <= n; i++) a[i] = (ll)read();
    for (int i = 1, u, v, w; i < n; i++) u = read(), v = read(), w = read(), add(u, v, w), add(v, u, w);
    dfs(1, 0);
    ans = 1;
    getans(1, 0, mi);
    printf("%d\n%lld\n", ans, mi);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zjlcnblogs/p/9277862.html
今日推荐