【SDOI2017】天才黑客

题面

题解

这是好久之前菊开讲的一道题目了。

可以发现在这道题目中,边比点更加重要,所以我们化边为点,将边权改为点权,边与边之间的边权就是题目所给的Trie树上LCA深度的和。

想到一个平方的暴力,每条边和它连向的点的出边连一条边。下一步考虑怎么优化。

对于每一个点,将它的入边和出边都拿出来,按照dfs序排序,那么可以考虑将每个点拆成入点和出点,因为\(\operatorname{lcp}(i, j)\)的值是\(i \to j\)之间所有的边的\(\operatorname{lcp}\)的最小值,于是前后缀优化连边就可以了。

可以考虑使用下面这个图来帮助理解:

【SDOI2017】天才黑客_第1张图片

说起来容易做起来难,代码留给读者作为练习

代码

#include 
#include 
#include 
#include 
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)

inline int read()
{
    int data = 0, w = 1; char ch = getchar();
    while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
    if (ch == '-') w = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = getchar();
    return data * w;
}

const int N(50010);
struct edge { int to, dis; };
int T, n, m, K, val[N * 20], pos[N], dis[N * 20], vis[N * 20], cur;
std::vector in[N], out[N];
std::vector G[N * 20];
namespace Tree
{
    int fa[N], size[N], heavy[N], dfn[N], bel[N], dep[N], cnt; std::vector T[N];
    inline void Add(int x, int y) { T[x].push_back(y); }
    void dfs(int x)
    {
        size[x] = 1, heavy[x] = 0;
        for (auto i : T[x])
        {
            fa[i] = x, dep[i] = dep[x] + 1, dfs(i), size[x] += size[i];
            if (size[heavy[x]] < size[i]) heavy[x] = i;
        }
    }

    void dfs(int x, int chain)
    {
        dfn[x] = ++cnt, bel[x] = chain;
        if (heavy[x]) dfs(heavy[x], chain);
        for (auto i : T[x]) if (i != heavy[x]) dfs(i, i);
    }

    int LCA(int x, int y)
    {
        for (; bel[x] != bel[y]; x = fa[bel[x]])
            if (dfn[bel[x]] < dfn[bel[y]]) std::swap(x, y);
        return dfn[x] < dfn[y] ? dep[x] : dep[y];
    }
}

void Dijkstra()
{
    std::priority_queue, std::vector >,
        std::greater > > Q;
    memset(dis, 127, (cur + 1) << 2), memset(vis, 0, sizeof vis);
    for (auto i : out[1]) Q.push(std::make_pair(dis[i] = val[i], i));
    while (!Q.empty())
    {
        int x = Q.top().second; Q.pop(); if (vis[x]) continue; vis[x] = 1;
        for (auto i : G[x]) if (dis[x] + val[i.to] + i.dis < dis[i.to])
            Q.push(std::make_pair(dis[i.to] = dis[x] + val[i.to] + i.dis, i.to));
    }
}

int Abs(int x) { return x < 0 ? -x : x; }
int cmp(int x, int y) { return Tree::dfn[pos[Abs(x)]] < Tree::dfn[pos[Abs(y)]]; }
void Link(int x)
{
    static int stk[N], p1[N], p2[N], q1[N], q2[N]; int top = 0;
    for (auto i : in[x]) stk[++top] = i;
    for (auto i : out[x]) stk[++top] = -i;
    std::sort(stk + 1, stk + top + 1, cmp);
    for (int i = 1; i <= top; i++)
        p1[i] = ++cur, p2[i] = ++cur, q1[i] = ++cur, q2[i] = ++cur;
    for (int i = 2; i <= top; i++)
        G[p1[i - 1]].push_back((edge) {p1[i], 0}),
        G[q1[i - 1]].push_back((edge) {q1[i], 0}),
        G[p2[i]].push_back((edge) {p2[i - 1], 0}),
        G[q2[i]].push_back((edge) {q2[i - 1], 0});
    for (int i = 1; i <= top; i++)
        if (stk[i] > 0) G[stk[i]].push_back((edge) {p1[i], 0}),
                        G[stk[i]].push_back((edge) {p2[i], 0});
        else stk[i] = -stk[i], G[q1[i]].push_back((edge) {stk[i], 0}),
                               G[q2[i]].push_back((edge) {stk[i], 0});
    for (int i = 1, x; i < top; i++)
        x = Tree::LCA(pos[stk[i]], pos[stk[i + 1]]),
        G[p1[i]].push_back((edge) {q1[i + 1], x}),
        G[p2[i + 1]].push_back((edge) {q2[i], x});
}

int main()
{
#ifndef ONLINE_JUDGE
    file(cpp);
#endif
    for (T = read(); T--; )
    {
        n = read(), m = cur = read(), K = read();
        memset(val, 0, sizeof val), Tree::cnt = 0;
        for (int i = 1, x, y; i <= m; i++)
            x = read(), y = read(), val[i] = read(), pos[i] = read(),
            out[x].push_back(i), in[y].push_back(i);
        for (int i = 1, x, y; i < K; i++)
            x = read(), y = read(), read(), Tree::Add(x, y);
        Tree::dfs(1), Tree::dfs(1, 1);
        for (int i = 2; i <= n; i++) Link(i);
        Dijkstra();
        for (int i = 2; i <= n; i++)
        {
            int ans = 2e9;
            for (auto x : in[i]) ans = std::min(ans, dis[x]);
            printf("%d\n", ans);
        }
        for (int i = 1; i <= cur; i++) G[i].clear();
        for (int i = 1; i <= n; i++) in[i].clear(), out[i].clear();
        for (int i = 1; i <= K; i++) Tree::T[i].clear();
    }
    return 0;
}

你可能感兴趣的:(【SDOI2017】天才黑客)