牛客-小V和gcd树

题目传送门

sol:树剖解决,我们只维护每个节点和重儿子的边权,那么当一个节点权值改变时,也只需要修改该节点和其重儿子的边权,若该节点是其父亲节点的重儿子,则多修改一条该节点和其父亲节点的边权。查询操作时,若该节点不是父亲节点的重儿子,则暴力计算一次该节点和父亲节点的边权。

  • 树链剖分
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    const int MAXN = 20010;
    inline int read() {
        int n = 0, f = 1; char c = getchar();
        while (c < '0' || c > '9') {
            if (c == '-') f = -f;
            c = getchar();
        }
        while (c >= '0' && c <= '9') {
            n = 10 * n + (c ^ '0');
            c = getchar();
        }
        return f * n;
    }
    struct {
        int v, to;
    } edge[2 * MAXN];
    int head[MAXN];
    void add_edge(int u, int v) {
        static int tot = 0;
        edge[tot].v = v;
        edge[tot].to = head[u];
        head[u] = tot ++;
    }
    int fat[MAXN], son[MAXN], siz[MAXN], val[MAXN];
    void dfs1(int u, int f) {
        fat[u] = f, siz[u] = 1;
        for (int i = head[u]; i != -1; i = edge[i].to) {
            int v = edge[i].v;
            if (v == f) continue;
            dfs1(v, u);
            siz[u] += siz[v];
            if (siz[v] > siz[son[u]]) son[u] = v;
        }
    }
    int gcd[MAXN], dep[MAXN], top[MAXN], dfn[MAXN];
    void dfs2(int u, int t) {
        static int tot = 0;
        dfn[u] = ++ tot;
        dep[u] = dep[fat[u]] + 1;
        top[u] = t;
        if (t != u) gcd[dfn[u]] = __gcd(val[u], val[fat[u]]);
        if (son[u]) dfs2(son[u], t);
        for (int i = head[u]; i != -1; i = edge[i].to) {
            int v = edge[i].v;
            if (v == fat[u] || v == son[u]) continue;
            dfs2(v, v);
        }
    }
    int query(int u, int v, int k) {
        int ans = 0;
        while (top[u] != top[v]) {
            if (dep[top[u]] > dep[top[v]]) swap(u, v);
            for (int i = dfn[top[v]] + 1; i <= dfn[v]; i++)
                ans += gcd[i] <= k;
            ans += __gcd(val[top[v]], val[fat[top[v]]]) <= k;
            v = fat[top[v]];
        }
        if (dep[u] > dep[v]) swap(u, v);
        for (int i = dfn[u] + 1; i <= dfn[v]; i++)
                ans += gcd[i] <= k;
        return ans;
    }
    int main() {
        int n = read(), m = read();
        memset(head, -1, sizeof(head));
        for (int i = 1; i <= n; i++) val[i] = read();
        for (int i = 2; i <= n; i++) {
            int u = read(), v = read();
            add_edge(u, v), add_edge(v, u);
        }
        dfs1(1, 0), dfs2(1, 1);
        for (int i = 1; i <= m; i++) {
            if (read() == 1) {
                int u = read(), k = read();
                val[u] = k;
                if (top[u] != u) {
                    gcd[dfn[u]] = __gcd(val[u], val[fat[u]]);
                }
                if (son[u]) {
                    gcd[dfn[son[u]]] = __gcd(val[u], val[son[u]]);
                }
            } else {
                int u = read(), v = read(), k = read();
                printf("%d\n", query(u, v, k));
            }
        }
        return 0;
    }

    ------------------------------------------------------------分隔线------------------------------------------------------------

    总结:$son,siz$必须在$dfs1$里求出;$dfn,top$必须在$dfs2$里求出;这次的错误是把$dfn$写到$dfs1$里面了,但是两次深搜的顺序是不一样的。第一次深搜就是按边的输入顺序进行搜索,第二次深搜在此基础上还优先搜索了重儿子。也正是因为优先搜索重儿子,才能保证重链的$dfs$序是连续的。

posted @ 2020-05-18 09:52  Jathon-cnblogs  阅读(329)  评论(0编辑  收藏  举报