美文网首页
[NOIP2016]天天爱跑步

[NOIP2016]天天爱跑步

作者: frankchen03 | 来源:发表于2019-10-06 08:27 被阅读0次

    题面传送门

    想到思路以后其实不难……

    解答

    设第j号玩家在V_j时刻出发。

    弱化问题:如果树退化成了一条链。则在j处的观察员能观察到的i号玩家当且仅当
    i玩家经过j,且 \begin{cases} dep_j - W_j = dep_{S_i} - V_j, &i向下跑步 \\ dep_j + W_j = dep_{S_i} + V_j, &i向上跑步 \end{cases}
    一个点在树上的贡献是连续的,可以考虑把路径拆成两条链用离线+树上差分统计人经过每个点的时刻。

    树上差分

    如何只让一段树上的链被更新呢?(类比左闭右开区间,考虑下端为“闭”节点,上端为“开”节点的链)

    考虑树上的一个点,只有链的一端在它的子树内部时才被链覆盖。
    所以在dfs子树之前记下答案,在dfs之后更新链端点,真的答案就是答案的差。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int mx = 3e5+10, LOG = 20, inf = 1e9;
    
    int n, m, ans[mx], s[mx], t[mx], w[mx];
    int head[mx], nxt[mx<<1], to[mx<<1], tot;
    int dep[mx] = {-1}, fa[mx][LOG];
    int down[mx<<2], up[mx<<1];
    vector<pair<int, int> > v[mx];
    
    void add(int u, int v) {
        ++tot;
        nxt[tot] = head[u];
        to[tot] = v;
        head[u] = tot;
    }
    
    void dfs0(int u) {
        for (int i = 1; i < LOG; ++i) {
            fa[u][i] = fa[fa[u][i-1]][i-1];
        }
        for (int e = head[u], v; e; e = nxt[e]) {
            if ((v=to[e]) == fa[u][0]) continue;
            fa[v][0] = u;
            dep[v] = dep[u]+1;
            dfs0(v);
        }
    }
    
    int lca(int u, int v) {
        if (dep[u] < dep[v]) swap(u, v);
        for (int i = LOG-1; ~i; --i)
            if (dep[fa[u][i]] >= dep[v])
                u = fa[u][i];
        if (u == v) return u;
        for (int i = LOG-1; ~i; --i)
            if (fa[u][i] != fa[v][i])
                u = fa[u][i], v = fa[v][i];
        return fa[u][0];
    }
    
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    
    void addr(int s, int t, int w=0) {
        assert(dep[s] != dep[t]);
        if (dep[s] < dep[t]) w += inf, swap(s, t);
        v[s].pb(mp(w, 1));
        v[t].pb(mp(w, -1));
    }
    
    void dfs(int u) {
        ans[u] -= up[dep[u]+w[u]] + down[(mx<<1) + dep[u]-w[u]];
        for (int e = head[u]; e; e = nxt[e]) {
            if (to[e] == fa[u][0]) continue;
            dfs(to[e]);
        }
        for (int i = 0; i < (int)v[u].size(); ++i) {
            if (v[u][i].fi > inf/2) down[(mx<<1) + v[u][i].fi-inf] += v[u][i].se;
            else up[v[u][i].fi] += v[u][i].se;
        }
        ans[u] += up[dep[u]+w[u]] + down[(mx<<1) + dep[u]-w[u]];
    }
    
    int main() {
        scanf("%d%d", &n, &m);
        int u, v;
        for (int i = 0; i < n-1; ++i) {
            scanf("%d%d", &u, &v);
            add(u, v), add(v, u);
        }
        dfs0(1);
        for (int i = 1; i <= n; ++i) scanf("%d", w+i);
        for (int i = 1; i <= m; ++i) {
            scanf("%d%d", s+i, t+i);
            u = lca(s[i], t[i]);
            if (u == s[i]) addr(fa[s[i]][0], t[i], dep[s[i]]);
            else if (u == t[i]) addr(s[i], fa[t[i]][0], dep[s[i]]);
            else {
                addr(s[i], fa[u][0], dep[s[i]]);
                addr(u, t[i], dep[u] - (dep[s[i]]-dep[u]));
            }
        }
        dfs(1);
        for (int i = 1; i <= n; ++i) printf("%d%c", ans[i], " \n"[i==n]);
    }
    

    相关文章

      网友评论

          本文标题:[NOIP2016]天天爱跑步

          本文链接:https://www.haomeiwen.com/subject/wbfupctx.html