[BZOJ2599][IOI2011]Race(点分治)

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=2599

Solution

看到 K 10 6 ,考虑如何求经过一个点 u 的长度为 K 的路径的边数最小值。
u 为根,顺序遍历 u 的每个子树,计算每个点到 u 的距离,称为 d i s 。用一个数组 o r z ,保存对于每个 i ,所有遍历过的子树内的点(包括 u )中满足 d i s [ x ] = i 的点 x 的( x 的深度 u 的深度)的最小值。遍历之前只有 u 被遍历过,因此 o r z [ 0 ] = 0 , 对于所有的 i [ 1 , K ] o r z [ i ] =
如果当前在遍历 v u 的子节点)的子树。那么 o r z 里保存了 u v 之前的子树(不包括 v 的子树)内的信息。而如果 v 的子树内有一个点 w w u 的距离为 x w u 经过的边数为 y ,那么可以设想,存在一条路径:
p u v 之前的子树内, p u 的距离为 K x u w 的距离为 x ,此路径为 p > w
那么,就可以用:

o r z [ K x ] + d e p t h [ w ] d e p t h [ u ]

更新答案。
遍历完 v 的子树后,还要对于子树内的每个点 w
o r z [ d i s [ w ] ] min ( o r z [ d i s [ w ] ] , d e p t h [ w ] d e p t h [ u ] )

但题目求的不仅仅是经过 u 的路径,因此用点分治,每次对分治出的子树都进行上述操作。 复杂度 O ( n log n + K )

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Edge(u) for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 2e5 + 5, M = N << 1, E = 1e6 + 5, INF = 0x3f3f3f3f;
int n, K, ecnt, nxt[M], adj[N], go[M], val[M], sze[N], maxs[N], dis[N], orz[E],
ans = INF, tot, sp[N], dep[N]; bool vis[N];
void add_edge(int u, int v, int w) {
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v; val[ecnt] = w;
    nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u; val[ecnt] = w;
}
void dfs1(int u, int fu) {
    sze[u] = 1; maxs[u] = 0; Edge(u) if (v != fu && !vis[v]) dfs1(v, u),
        sze[u] += sze[v], maxs[u] = max(maxs[u], sze[v]);
}
void dfs2(int u, int fu, int r, int &G) {
    maxs[u] = max(maxs[u], sze[r] - sze[u]);
    Edge(u) if (v != fu && !vis[v]) dfs2(v, u, r, G); if (maxs[u] < maxs[G]) G = u;
}
int calcG(int u) {int G = u; dfs1(u, 0); dfs2(u, 0, u, G); return G;}
void dfs(int u, int fu, int d) {
    if (dis[u] > K) return; sp[++tot] = dis[u]; dep[tot] = d;
    Edge(u) if (v != fu && !vis[v]) dis[v] = dis[u] + val[e], dfs(v, u, d + 1);
}
void xyz32768issoweak(int u) {
    int i, G = calcG(u); vis[G] = 1; orz[0] = tot = 0; Edge(G) if (!vis[v]) {
        int tmp = tot; dis[v] = val[e]; dfs(v, G, 1);
        For (i, tmp + 1, tot) ans = min(ans, dep[i] + orz[K - sp[i]]);
        For (i, tmp + 1, tot) orz[sp[i]] = min(orz[sp[i]], dep[i]);
    }
    orz[0] = INF; For (i, 1, tot) orz[sp[i]] = INF;
    Edge(G) if (!vis[v]) xyz32768issoweak(v);
}
int main() {
    memset(orz, INF, sizeof(orz));
    int i, x, y, z; n = read(); K = read(); For (i, 1, n - 1)
        x = read() + 1, y = read() + 1, z = read(), add_edge(x, y, z);
    if (K == 0) return puts("0"), 0; xyz32768issoweak(1);
    if (ans == INF) return puts("-1"), 0; cout << ans << endl; return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/80866724