zoukankan      html  css  js  c++  java
  • [NOIp2016]天天爱跑步 线段树合并

    [NOIp2016]天天爱跑步

    LG传送门

    对于一个人,他的路程会分为两段,一段向上(根),一段向下,考虑在向上过程中他能产生贡献的观察者具有什么性质:设出发点深度为(dep[x]),观察者深度为(dep[y]),观察的时间为(t),需满足(dep[x] - dep[y] = t),换句话说就是(dep[y] + t = dep[x])。向下那一段的推导类似,下面的部分也只以向上的路径为例来解释。

    现在我们记录每个点下方出发点深度为(dep[x])的人数,需要对于每个出发点更新出发点到出发点与目的地的lca的所有点,想到到开一颗权值线段树,用线段树合并不断把信息往上传,在lca处消除这次更新的影响,这样一来直接我们就可以在树上统计答案了。向下路径的处理与向上路径类似,这里就不再赘述,上代码。

    #include <cstdio>
    #include <cctype>
    #include <vector>
    #define R register
    #define I inline
    #define B 1000000
    using namespace std;
    const int N = 300001, M = 15000007;
    char buf[B], *p1, *p2;
    I char gc() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, B, stdin), p1 == p2) ? EOF : *p1++; }
    I int rd() {
        R int f = 0;
        R char c = gc();
        while (c < 48 || c > 57)
            c = gc();
        while (c > 47 && c < 58)
            f = f * 10 + (c ^ 48), c = gc();
        return f;
    }
    int w[N], s[N], t[N], p[N], q[N], d[N], r[N], u[N], v[N], o[N], n, C, S;
    struct T{int f, p, q;}e[M];
    vector <int> g[N];
    I void swp(int &x, int &y) { x ^= y, y ^= x, x ^= y; }
    void dfs1(int x, int f) {
        p[x] = f, d[x] = d[f] + 1, t[x] = 1;
        for (R int i = 0, y, m = 0; i < s[x]; ++i)
            if ((y = g[x][i]) ^ f) {
                dfs1(y, x), t[x] += t[y];
                if (t[y] > m)
                    m = t[y], q[x] = y;
            }
    }
    void dfs2(int x, int t) {
        r[x] = t;
        if (q[x])
            dfs2(q[x], t);
        for (R int i = 0, y; i < s[x]; ++i)
            if ((y = g[x][i]) ^ p[x] && y ^ q[x])
                dfs2(y, y);
    }
    I int lca(int x, int y) {
        while (r[x] ^ r[y]) {
            if (d[r[x]] < d[r[y]])
                swp(x, y);
            x = p[r[x]];
        }
        if (d[x] > d[y])
            swp(x, y);
        return x;
    }
    void ins(int &k, int l, int r, int x, int v) {
        if (!k)
            k = ++C;
        e[k].f += v;
        if (l == r)
            return ;
        R int m = l + r >> 1;
        if (x <= m)
            ins(e[k].p, l, m, x, v);
        else
            ins(e[k].q, m + 1, r, x, v);
    }
    int mrg(int k, int t) {
        if (!k)
            return t;
        if (!t)
            return k;
        e[k].f += e[t].f, e[k].p = mrg(e[k].p, e[t].p), e[k].q = mrg(e[k].q, e[t].q);
        return k;
    }
    int qry(int k, int l, int r, int x) {
        if (l == r)
            return e[k].f;
        R int m = l + r >> 1;
        if (x <= m)
            return qry(e[k].p, l, m, x);
        else
            return qry(e[k].q, m + 1, r, x);
    }
    void dfs(int x) {
        for (R int i = 0, y; i < s[x]; ++i)
            if ((y = g[x][i]) ^ p[x])
                dfs(y), u[x] = mrg(u[x], u[y]), v[x] = mrg(v[x], v[y]);
        o[x] = qry(u[x], 1, S, w[x] + d[x]) + qry(v[x], 1, S, w[x] - d[x] + n);
    }
    int main() {
        R int m, i, x, y, a;
        n = rd(), m = rd(), S = n << 1;
        for (i = 1; i < n; ++i)
            x = rd(), y = rd(), g[x].push_back(y), g[y].push_back(x);
        for (i = 1; i <= n; ++i)
            s[i] = g[i].size(), w[i] = rd();
        dfs1(1, 0), dfs2(1, 1);
        for (i = 1; i <= m; ++i)
            x = rd(), y = rd(), a = lca(x, y), ins(u[x], 1, S, d[x], 1), ins(u[a], 1, S, d[x], -1), ins(v[y], 1, S, d[x] - (d[a] << 1) + n, 1), ins(v[p[a]], 1, S, d[x] - (d[a] << 1) + n, -1);
        dfs(1);
        for (i = 1; i <= n; ++i)
            printf("%d ", o[i]);
        return 0;
    }
    
    

    因为减的结果可能有负数,所以加上一个(n),值域变两倍。

  • 相关阅读:
    [CSP-S模拟测试]:attack(支配树+LCA+bitset)
    [杂题]:C/c(二分答案)
    [杂题]:B/b(二分答案)
    二维莫队(离线)
    [CSP-S模拟测试]:联盟(搜索+树的直径)
    [CSP-S模拟测试]:蔬菜(二维莫队)
    [CSP-S模拟测试]:施工(DP+单调栈+前缀和)
    [CSP-S模拟测试]:画作(BFS+数学)
    [CSP-S模拟测试]:折射(DP)
    [CSP-S模拟测试]:养花(分块)
  • 原文地址:https://www.cnblogs.com/cj-chd/p/10353138.html
Copyright © 2011-2022 走看看