美文网首页
[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]天天爱跑步

    题面传送门 想到思路以后其实不难…… 解答 设第号玩家在时刻出发。 弱化问题:如果树退化成了一条链。则在处的观察员...

  • 跑休真的很重要

    对于爱跑步的人,只要天气允许,天天都想跑步。而对于不爱跑步的人,跑步的人就像有病一样。不知疲倦的在操场那儿刷圈。当...

  • 好好生活,慢慢减脂之3月19日

    今早,没有下雨,于是决定去跑步,还是那几个跑友,热爱跑步的人天天在跑,也正因为有这些人,我也爱上了跑步。“爱非坚持...

  • 美好清晨20220408——育儿育己:学会克制

    起的早很想跑步,最终还是告诫自己:跑步受伤刚好,不能再天天跑了。喜欢是放肆,而爱是克制。 为了能健康长久的跑下去,...

  • ETOON漫画天天跑步

  • 天天写字,年年跑步

    每天100字,一年400公里。 我们普通人,把这个简单的“公式”放在生活里,天天写字,年年跑步,就会产生属于一种自...

  • 哥,你喝酒很牛逼!但我起床早呀,跑步快呀,身材好呀~

    (文 / 跑步女神) 跑友小王,昨晚参加饭局。几个爱喝酒的朋友,笑话小王,说他:“天天跑步,看着好傻,天天跑步啥意...

  • 我是一个物质的人

    买了新的跑步衣就想起来天天去跑步。 买了双新的运动鞋,就想着天天去跑步。 买了个小摩托,就想着天天去喜欢的公园,天...

  • 你好,我是妈妈的布丁狗

    布丁狗性别男爱妹纸爱妈妈妈妈说努力奔跑就会遇到小白,我天天努力跑步可还是米遇到小白,我怀疑妈妈骗狗。

  • 天天爱

    今生挚爱的那个他 已成为我生命中不可缺少的一部分。 因为他我的生命会越来越精彩。 一辈子那么长。 我只会喜欢你一个...

网友评论

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

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