• 数据结构题锦——树链


    树链:

    树链求最近公共祖先

    最近公共祖先

    题目:

    题目描述

    如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

    输入格式

    第一行包含三个正整数 N,M,S,分别表示树的结点个数、询问的个数和树根结点的序号。

    接下来 N1 行每行包含两个正整数x,y,表示 x 结点和 y 结点之间有一条直接连接的边(数据保证可以构成树)。

    接下来 M 行每行包含两个正整数a,b,表示询问 a 结点和 b 结点的最近公共祖先。

    输出格式

    输出包含 M 行,每行包含一个正整数,依次为每一个询问的结果。

    输入输出样例

    输入 #1
    5 5 4
    3 1
    2 4
    5 1
    1 4
    2 4
    3 2
    3 5
    1 2
    4 5
    输出 #1
    4
    4
    1
    4
    4
    

    说明/提示

    对于 30% 的数据,N10,M10。

    对于 70% 的数据,N10000,M10000。

    对于 100% 的数据,N500000,M500000。

    样例说明:

    该树结构如下:

    第一次询问:2,4 的最近公共祖先,故为 4。

    第二次询问:3,2 的最近公共祖先,故为 4。

    第三次询问:3,5 的最近公共祖先,故为 1。

    第四次询问:1,2 的最近公共祖先,故为 4。

    第五次询问:4,5 的最近公共祖先,故为 4。

    故输出依次为 4, 4, 1, 4, 4

    分析:

    LCA:

    树上倍增法可以做,树链也可以做

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 500005, M = N << 1;
    int n, m, root;
    int w[N], h[N], e[M], ne[M], idx;
    int dep[N], fa[N], sz[N], son[N];
    int id[N], nw[N], top[N], tot;
    void add(int a, int b)
    {
      e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
    }
    void dfs1(int x, int father, int depth)
    {
      dep[x] = depth, fa[x] = father, sz[x] = 1;
      for (int i = h[x]; ~i; i = ne[i])
      {
        int y = e[i];
        if (y == father) continue;
        dfs1(y, x, depth + 1);
        sz[x] += sz[y];
        if (sz[son[x]] < sz[y]) son[x] = y;
      }
    }
    void dfs2(int x, int t)
    {
      id[x] = ++ tot, nw[tot] = w[x], top[x] = t;
      if (!son[x]) return;
      dfs2(son[x], t);
      for (int i = h[x]; ~i; i = ne[i])
      {
        int y = e[i];
        if (y == fa[x] || y == son[x]) continue;
        dfs2(y, y);
      }
    }
    int lca(int u, int v)
    {
      while (top[u] != top[v])
      {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        u = fa[top[u]];
      }
      if (dep[u] < dep[v]) swap(u, v);
      return v;
    }
    int main()
    {
      memset(h, -1, sizeof h);
      scanf("%d%d%d", &n, &m, &root);
      for (int i = 0; i < n - 1; i ++ )
      {
        int a, b; scanf("%d%d", &a, &b);
        add(a, b); add(b, a);
      }
      dfs1(root, -1, 1);
      dfs2(root, root);
      while (m -- )
      {
        int x, y; scanf("%d%d", &x, &y);
        printf("%d
    ", lca(x, y));
      }
      return 0;
    }
    

     

    2568. 树链剖分

    题目:

    给定一棵树,树中包含 nn 个节点(编号 1∼n),其中第 ii 个节点的权值为 ai。

    初始时,1 号节点为树的根节点。

    现在要对该树进行 m 次操作,操作分为以下 4种类型:

    • 1 u v k,修改路径上节点权值,将节点 和节点 v、v 之间路径上的所有节点(包括这两个节点)的权值增加 kk。
    • 2 u k,修改子树上节点权值,将以节点 u 为根的子树上的所有节点的权值增加 k
    • 3 u v,询问路径,询问节点 u 和节点 v 之间路径上的所有节点(包括这两个节点)的权值和。
    • 4 u,询问子树,询问以节点 u 为根的子树上的所有节点的权值和。

    输入格式

    第一行包含一个整数 n,表示节点个数。

    第二行包含 n 个整数,其中第 i 个整数表示 ai。

    接下来 n 行,每行包含两个整数 x,y,表示节点 x 和节点 y 之间存在一条边。

    再一行包含一个整数 m,表示操作次数。

    接下来 m 行,每行包含一个操作,格式如题目所述。

    输出格式

    对于每个操作 3 和操作 4,输出一行一个整数表示答案。

    数据范围

    1≤n,m≤105,
    0≤ai,k≤105,
    1≤u,v,x,y≤n

    输入样例:

    5
    1 3 7 4 5
    1 3
    1 4
    1 5
    2 3
    5
    1 3 4 3
    3 5 4
    1 3 5 10
    2 3 5
    4 1
    

    输出样例:

    16
    69

     

    分析:

    树上任意两点之间查询和修改,对整棵子树进行查询和修改

    树链+线段树

    树链可以将一棵树转化成序列,使得树中任意一条路径可以分割成不超过logn段连续的序列。

    如果是对一个子树求改和查询的话,就是对子树根节点维护,因为DFS在搜索的时候,就是一颗一颗子树搜索的,所以,一棵子树内的序列好是连续的

    然后,问题就转化为了对连续区间的查询和修改,用线段树维护

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 100010, M = N << 1;
    int n, m;
    int w[N], h[N], e[M], ne[M], idx;
    int id[N], nw[N], tot;
    int dep[N], sz[N], top[N], fa[N], son[N];
    struct Node{
      int l, r;
      ll add, sum;
    }tr[N << 2];
    void add(int a, int b)
    {
      e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
    }
    void dfs1(int x, int father, int depth)
    {
      dep[x] = depth, fa[x] = father, sz[x] = 1;
      for (int i = h[x]; ~i; i = ne[i])
      {
        int y = e[i];
        if (y == father) continue;
        dfs1(y, x, depth + 1);
        sz[x] += sz[y];
        if (sz[son[x]] < sz[y]) son[x] = y;
      }
    }
    void dfs2(int x, int t)
    {
      //重链的top一定是一个轻儿子
      id[x] = ++ tot, nw[tot] = w[x], top[x] = t;
      if (!son[x]) return;
      dfs2(son[x], t);
      for (int i = h[x]; ~i; i = ne[i])
      {
        int y = e[i];
        if (y == fa[x] || y == son[x]) continue;
        dfs2(y, y);
      }
    }
    void pushup(int u)
    {
      tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
    }
    void pushdown(int u)
    {
      Node &c = tr[u], &a = tr[u << 1], &b = tr[u << 1 | 1];
      if (c.add)
      {
        a.sum += c.add * (a.r - a.l + 1);
        a.add += c.add;
        b.sum += c.add * (b.r - b.l + 1);
        b.add += c.add;
        c.add = 0;
      }
    }
    void build(int u, int l, int r){
      if (l == r)
      {
        tr[u] = {l, r, 0, nw[l]};
        return;
      }
      tr[u] = {l, r};
      int mid = l + r >> 1;
      build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r);
      pushup(u);
    }
    void update(int u, int l, int r, int c)
    {
      if (tr[u].l >= l && tr[u].r <= r)
      {
        tr[u].sum += (tr[u].r - tr[u].l + 1) * c;
        tr[u].add += c;
        return;
      }
      pushdown(u);
      int mid = tr[u].l + tr[u].r >> 1;
      if (l <= mid) update(u << 1, l, r, c);
      if (r > mid) update(u << 1 | 1, l, r, c);
      pushup(u);
    }
    ll query(int u, int l, int r)
    {
      if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
      pushdown(u);
      ll res = 0;
      int mid = tr[u].l + tr[u].r >> 1;
      if (l <= mid) res += query(u << 1, l, r);
      if (r > mid) res += query(u << 1 | 1, l, r);
      return res;
    }
    void update_path(int u, int v, int c)
    {
      //给路径从u到v都+c,显然分为在同一重链和不在同一重链上
      //top不同,表明不在同一重链上
      while (top[u] != top[v])
      {
        //让u的重链的头结点在下面
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        //更新u的从u位置一直到top[u],注意,DFS序前者大后者小
        update(1, id[top[u]], id[u], c);
        u = fa[top[u]];
      }
      //找到u和v更新到在同一条重链上
      if (dep[u] < dep[v]) swap(u, v);
      update(1, id[v], id[u], c);
    }
    ll query_path(int u, int v)
    {
      ll res = 0;
      while (top[u] != top[v])
      {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        res += query(1, id[top[u]], id[u]);
        u = fa[top[u]];
      }
      if (dep[u] < dep[v]) swap(u, v);
      res += query(1, id[v], id[u]);
      return res;
    }
    void update_tree(int u, int c)
    {
      //因为DFS是一棵子树搜完在搜其他子树,所以,一棵子树内的所有节点的DFS序是一段连续的区间
      update(1, id[u], id[u] + sz[u] - 1, c);
    }
    ll query_tree(int u)
    {
      return query(1, id[u], id[u] + sz[u] - 1);
    }
    int main()
    {
      scanf("%d", &n);
      for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
      memset(h, -1, sizeof h);
      for (int i = 0; i < n - 1; i ++ )
      {
        int a, b; scanf("%d%d", &a, &b);
        add(a, b); add(b, a);
      }
      dfs1(1, -1, 1);
      dfs2(1, 1);
      build(1, 1, n);
      scanf("%d", &m);
      while (m -- )
      {
        int t, u, v, k;
        scanf("%d%d", &t, &u);
        if (t == 1)
        {
          scanf("%d%d", &v, &k);
          update_path(u, v, k);
        }
        else if (t == 2)
        {
          scanf("%d", &k);
          update_tree(u, k);
        }
        else if (t == 3)
        {
          scanf("%d", &v);
          printf("%lld
    ", query_path(u, v));
        }
        else
        {
          printf("%lld
    ", query_tree(u));
        }
      }
      return 0;
    }
    

      

     918. 软件包管理器

    P2146 [NOI2015] 软件包管理器

    Linux 用户和 OSX 用户一定对软件包管理器不会陌生。

    通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖(即下载安装这个软件包的安装所依赖的其它软件包),完成所有的配置。

    Debian/Ubuntu 使用的 apt-get,Fedora/CentOS 使用的 yum,以及 OSX 下可用的 homebrew 都是优秀的软件包管理器。

    你决定设计你自己的软件包管理器。

    不可避免地,你要解决软件包之间的依赖问题。

    如果软件包 A 依赖软件包 B,那么安装软件包 A 以前,必须先安装软件包 B。

    同时,如果想要卸载软件包 B,则必须卸载软件包 A。

    现在你已经获得了所有的软件包之间的依赖关系。

    而且,由于你之前的工作,除0 号软件包以外,在你的管理器当中的软件包都会依赖一个且仅一个软件包,而 0 号软件包不依赖任何一个软件包。

    依赖关系不存在环(若有 m(m≥2) 个软件包 A1,A2,A3,…,Am,其中 A1 依赖 A2,A2 依赖 A3,A3 依赖 A4,……,Am−1 依赖 Am,而 Am 依赖 A1,则称这 m 个软件包的依赖关系构成环),当然也不会有一个软件包依赖自己。

    现在你要为你的软件包管理器写一个依赖解决程序。

    根据反馈,用户希望在安装和卸载某个软件包时,快速地知道这个操作实际上会改变多少个软件包的安装状态(即安装操作会安装多少个未安装的软件包,或卸载操作会卸载多少个已安装的软件包),你的任务就是实现这个部分。

    注意,安装一个已安装的软件包,或卸载一个未安装的软件包,都不会改变任何软件包的安装状态,即在此情况下,改变安装状态的软件包数为 00。

    输入格式

    输入文件的第 1 行包含 1 个正整数 n,表示软件包的总数。软件包从 0 开始编号。

    随后一行包含 n−1 个整数,相邻整数之间用单个空格隔开,分别表示 1,2,3,…,n−2,n−1 号软件包依赖的软件包的编号。

    接下来一行包含 1 个正整数 q,表示询问的总数。

    之后 q 行,每行 1 个询问。询问分为两种:

    • install x:表示安装软件包 xx
    • uninstall x:表示卸载软件包 xx

    你需要维护每个软件包的安装状态,一开始所有的软件包都处于未安装状态。

    对于每个操作,你需要输出这步操作会改变多少个软件包的安装状态,随后应用这个操作(即改变你维护的安装状态)。

    输出格式

    输出文件包括 qq 行。

    输出文件的第 ii 行输出 11 个整数,为第 ii 步操作中改变安装状态的软件包数。

    数据范围

    1130_c73c6078ae-软件包管理器2.png

    输入样例1:

    7
    0 0 0 1 1 5
    5
    install 5
    install 6
    uninstall 1
    install 4
    uninstall 0
    

    输出样例1:

    3
    1
    3
    2
    3
    

    输入样例2:

    10
    0 1 2 1 3 0 0 3 2
    10
    install 0
    install 3
    uninstall 2
    install 7
    install 5
    install 9
    uninstall 9
    install 4
    install 1
    install 9
    

    输出样例2

    1
    3
    2
    1
    3
    1
    1
    1
    0
    1
    
     

    分析:

    两种操作,

    1:将根节点到当前节点的权值,全部变成1

    2:将当前子树的权值,全部变成0

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 100005;
    int n, m;
    int h[N], e[N], ne[N], idx;
    int fa[N], son[N], sz[N], dep[N];
    int id[N], top[N], tot;
    struct Node{
        int l, r;
        int flag, sum;
    }tr[N << 2];
    void add(int a, int b)
    {
        e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
    }
    void dfs1(int x, int depth)
    {
        dep[x] = depth, sz[x] = 1;
        for (int i = h[x]; ~i; i = ne[i])
        {
            int y = e[i];
            dfs1(y, depth + 1);
            sz[x] += sz[y];
            if (sz[son[x]] < sz[y]) son[x] = y;
        }
    }
    void dfs2(int x, int t)
    {
        id[x] = ++ tot, top[x] = t;
        if (!son[x]) return;
        dfs2(son[x], t);
        for (int i = h[x]; ~i; i = ne[i])
        {
            int y = e[i];
            if (y == son[x]) continue;
            dfs2(y, y);
        }
    }
    void pushup(int u)
    {
        tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
    }
    void pushdown(int u)
    {
        Node &c = tr[u], &a = tr[u << 1], &b = tr[u << 1 | 1];
        if (c.flag != -1)
        {
            a.sum = c.flag * (a.r - a.l + 1);
            b.sum = c.flag * (b.r - b.l + 1);
            a.flag = b.flag = c.flag;
            c.flag = -1;
        }
    }
    void build(int u, int l, int r)
    {
        if (l == r)
        {
            tr[u] = {l,r, -1, 0};
            return;
        }
        tr[u] = {l, r};
        int mid = l + r >> 1;
        build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
    void update(int u, int l, int r, int c)
    {
        if (tr[u].l >= l && tr[u].r <= r)
        {
            tr[u].sum = c * (tr[u].r - tr[u].l + 1);
            tr[u].flag = c;
            return;
        }
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) update(u << 1, l, r, c);
        if (r > mid) update(u << 1 | 1, l, r, c);
        pushup(u);
    }
    int query(int u, int l, int r)
    {
        if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
        pushdown(u);
        int res = 0;
        int mid = tr[u].l + tr[u].l >> 1;
        if (l <= mid) res += query(u << 1, l, r);
        if (r > mid) res += query(u << 1 | 1, l, r);
        return res;
    }
    void update_path(int u, int v, int c)
    {
        while (top[u] != top[v])
        {
            if (dep[top[u]] < dep[top[v]]) swap(u, v);
            update(1, id[top[u]], id[u], c);
            u = fa[top[u]];
        }
        if (dep[u] < dep[v]) swap(u, v);
        update(1, id[v], id[u], c);
    }
    void update_tree(int u, int c)
    {
        update(1, id[u], id[u] + sz[u] - 1, c);
    }
    int main()
    {
        memset(h, -1, sizeof h);
        cin >> n;
        for (int i = 2; i <= n; i ++ )
        {
            int x; scanf("%d", &x);
            x ++;
            add(x, i);
            fa[i] = x;
        }
        dfs1(1, 1);
        dfs2(1, 1);
        build(1, 1, n);
        cin >> m;
        char op[20]; int x;
        while (m -- )
        {
            scanf("%s%d", op, &x);
            x ++;
            if (!strcmp(op, "install"))
            {
                int sum = query(1, 1, n);
                update_path(1, x, 1);
                int ans = query(1, 1, n) - sum;
                printf("%d
    ", ans);
            }
            else 
            {
                int sum = query(1, 1, n);
                update_tree(x, 0);
                int  ans = sum - query(1, 1, n);
                printf("%d
    ", ans);
            }
        }
        return 0;
    }
    

      

    P2486 [SDOI2011]染色

    题目:

    题目描述

    给定一棵 n 个节点的无根树,共有mm 个操作,操作分为两种:

    1. 将节点 a 到节点 b 的路径上的所有点(包括 a 和 b)都染成颜色 c。
    2. 询问节点 a 到节点 b 的路径上的颜色段数量。

    颜色段的定义是极长的连续相同颜色被认为是一段。例如 112221 由三段组成:112221

    输入格式

    输入的第一行是用空格隔开的两个整数,分别代表树的节点个数 n 和操作个数 m。

    第二行有 n 个用空格隔开的整数,第 i 个整数wi 代表结点 i 的初始颜色。

    第 3 到第 (n+1) 行,每行两个用空格隔开的整数 u,v,代表树上存在一条连结节点 u 和节点 v 的边。

    第 (n+2) 到第 (n+m+1) 行,每行描述一个操作,其格式为:

    每行首先有一个字符 op,代表本次操作的类型。

    • 若 op 为 C,则代表本次操作是一次染色操作,在一个空格后有三个用空格隔开的整数 a,b,c,代表将 a 到 b 的路径上所有点都染成颜色 cc。
    • 若 op 为 Q,则代表本次操作是一次查询操作,在一个空格后有两个用空格隔开的整数 a,b,表示查询 a 到 b 路径上的颜色段数量。

    输出格式

    对于每次查询操作,输出一行一个整数代表答案。

    输入输出样例

    输入 #1
    6 5
    2 2 1 2 1 1
    1 2
    1 3
    2 4
    2 5
    2 6
    Q 3 5
    C 2 1 1
    Q 3 5
    C 5 1 2
    Q 3 5
    
    输出 #1
    3
    1
    2
    

    说明/提示

    数据规模与约定

    对于 100% 的数据,1n,m105,1wi,c109,1a,b,u,vn,op 一定为 C 或 Q,保证给出的图是一棵树。

    除原数据外,还存在一组不计分的 hack 数据。

    分析:

    树剖后,用线段树维护左端点l,右端点r,左端点颜色lc,右端点rc,区间更新颜色的标记tag,区间颜色的段树num。

    现在对于维护线段树需要注意的是,在合并左子树和右子树的时候,左子树的右端点如果和右子树的左端点的颜色相同,那么,合并后的二叉线段树的num需要减一。

    下面考虑树剖的问题, 如果当前树剖到的链与上一次的链在相交的边缘颜色可能相等,如果相等,答案需要减一。所以,统计答案的时候,需要记录一下上一次剖到的链的左端点的颜色(左端点指的是索引小的那头,也就是子树的树根位置),与当前树剖的右端点的颜色,比较两个颜色,如果相同则答案减一。

    注意:由于u和v两个位置向上走,那么要记录ans1,ans2两个变量来存储上一次的左端点颜色。当top[u] = top[v]的时候,  已经在同一个重链上了,两边端点的颜色都要考虑与对应的ans比较颜色,相同答案要相应减一。

     代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 100005;
    int n, m;
    int w[N],h[N], e[N << 1], ne[N << 1], idx;
    int fa[N], son[N], sz[N], dep[N];
    int id[N], nw[N], top[N], tot;
    void add_edges(int a,int b)
    {
      e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
    }
    void dfs1(int x, int father, int depth)
    {
      dep[x] = depth, fa[x] = father, sz[x] = 1;
      for (int i = h[x]; ~i; i = ne[i])
      {
        int y = e[i];
        if (y == father) continue;
        dfs1(y, x, depth + 1);
        sz[x] += sz[y];
        if (sz[son[x]] < sz[y]) son[x] = y;
      }
    }
    void dfs2(int x, int t)
    {
      id[x] = ++ tot, nw[tot] = w[x], top[x] = t;
      if (!son[x]) return;
      dfs2(son[x], t);
      for (int i = h[x]; ~i; i = ne[i])
      {
        int y = e[i];
        if (y == fa[x] || y == son[x]) continue;
        dfs2(y, y);
      }
    }
    struct Node{
      int l, r;
      int num, tag, lc, rc;
    }tr[N << 2];
    int Lc, Rc;
    void pushup(int u)
    {
      Node &c = tr[u], &a = tr[u << 1], &b = tr[u << 1 | 1];
      c.lc = a.lc, c.rc = b.rc;
      c.num = a.num + b.num;
      if (a.rc == b.lc) c.num --;
    }
    void pushdown(int u)
    {
      Node &c = tr[u], &a = tr[u << 1], &b = tr[u << 1 | 1];
      if (c.tag)
      {
        a.num = b.num = c.num;
        a.lc = a.rc = b.lc = b.rc = c.lc;
        a.tag = b.tag = c.tag;
        c.tag = 0;
      }
    }
    void build(int u, int l, int r)
    {
      tr[u] = {l, r, 0, 0, 0, 0};
      if (l == r) return;
      int mid = l + r >> 1;
      build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    }
    void update(int u, int l, int r, int c)
    {
      if (tr[u].l == l && tr[u].r == r)
      {
        tr[u].tag = tr[u].num = 1;
        tr[u].lc = tr[u].rc = c;
        return;
      }
      pushdown(u);
      int mid = tr[u].l + tr[u].r >> 1;
      if (r <= mid) update(u << 1, l, r, c);
      else if (l > mid) update(u << 1 | 1, l, r, c);
      else update(u << 1, l, mid, c), update(u << 1 | 1, mid + 1, r, c);
      pushup(u);
    }
    int query(int u, int l, int r, int L, int R)
    {
      if (tr[u].l == L)
        Lc = tr[u].lc;
      if (tr[u].r == R)
        Rc = tr[u].rc;
      if (tr[u].l == l && tr[u].r == r) return tr[u].num;
      pushdown(u);
      int mid = tr[u].l + tr[u].r >> 1;
      if (r <= mid) return query(u << 1, l, r, L, R);
      else if (l > mid) return query(u << 1 | 1, l, r, L, R);
      else
      {
        int ans = query(u << 1, l, mid, L, R) + query(u << 1 | 1, mid + 1, r, L, R);
        if (tr[u << 1].rc == tr[u << 1 | 1].lc) ans --;
        return ans;
      }
      pushup(u);
    }
    void update_path(int u, int v, int c)
    {
      while (top[u] != top[v])
      {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        update(1, id[top[u]], id[u], c);
        u = fa[top[u]];
      }
      if (dep[u] < dep[v]) swap(u, v);
      update(1, id[v], id[u], c);
    }
    int query_path(int u, int v)
    {
      int ans1 = -1, ans2 = -1;
      int ans = 0;
      while (top[u] != top[v])
      {
        if (dep[top[u]] < dep[top[v]]) swap(u, v), swap(ans1, ans2);
        ans += query(1, id[top[u]], id[u], id[top[u]], id[u]);
        //在swap后,ans1表示这颗树链下方的节点
        if (Rc == ans1) ans --;
        ans1 = Lc, u = fa[top[u]];
      }
      if (dep[u] < dep[v]) swap(u, v), swap(ans1, ans2);
      ans += query(1, id[v], id[u], id[v], id[u]);
      if (Rc == ans1) ans --;
      if (Lc == ans2) ans --;
      return ans;
    }
    int main()
    {
      memset(h, -1, sizeof h);
      scanf("%d%d", &n, &m);
      for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
      for (int i = 0; i < n - 1; i ++ )
      {
        int a, b; scanf("%d%d", &a, &b);
        add_edges(a, b);
        add_edges(b, a);
      }
      dfs1(1, -1, 1);
      dfs2(1, 1);
      // printf("-------------
    ");
      build(1, 1, n);
      for (int i = 1; i <= n; i ++ ) update(1, id[i], id[i], w[i]);
      char op[2]; int l, r, c;
      while (m -- )
      {
        scanf("%s%d%d", op, &l, &r);
        if (*op == 'C')
        {
          scanf("%d", &c);
          update_path(l, r, c);
        }
        else
        {
          printf("%d
    ", query_path(l, r));
        }
      }
    }
    

      

    P2590 [ZJOI2008]树的统计

     题目:

    题目描述

    一棵树上有 n 个节点,编号分别为 1 到 n,每个节点都有一个权值 w。

    我们将以下面的形式来要求你对这棵树完成一些操作:

    I. CHANGE u t : 把结点 u 的权值改为 t

    II. QMAX u v: 询问从点 u 到点 v 的路径上的节点的最大权值。

    III. QSUM u v: 询问从点 u 到点 v 的路径上的节点的权值和。

    注意:从点 u 到点 v 的路径上的节点包括 u 和 v 本身。

    输入格式

    输入文件的第一行为一个整数 n,表示节点的个数。

    接下来 n1 行,每行 2 个整数 a 和 b,表示节点 a 和节点 b 之间有一条边相连。

    接下来一行 n 个整数,第 i 个整数 wi 表示节点 ii 的权值。

    接下来 1 行,为一个整数 q,表示操作的总数。

    接下来 q 行,每行一个操作,以 CHANGE u t 或者 QMAX u v 或者 QSUM u v 的形式给出。

    输出格式

    对于每个 QMAX 或者 QSUM 的操作,每行输出一个整数表示要求输出的结果。

    输入输出样例

    输入 #1
    4
    1 2
    2 3
    4 1
    4 2 1 3
    12
    QMAX 3 4
    QMAX 3 3
    QMAX 3 2
    QMAX 2 3
    QSUM 3 4
    QSUM 2 1
    CHANGE 1 5
    QMAX 3 4
    CHANGE 3 6
    QMAX 3 4
    QMAX 2 4
    QSUM 3 4
    
    输出 #1
    4
    1
    2
    2
    10
    6
    5
    6
    5
    16
    

    说明/提示

    对于 100% 的数据,保证 1n3×104,0q2×105。

    中途操作中保证每个节点的权值 w 在 3×104 到 3×104 之间。

    分析:

    这里的sum和max性质不同,还是写两个函数来求解吧

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 100005;
    int n, m;
    int w[N], h[N], e[N], ne[N << 1], idx;
    int fa[N], son[N], sz[N], dep[N];
    int id[N], nw[N], top[N], tot;
    void add_edges(int a, int b)
    {
      e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
    }
    void dfs1(int x, int father, int depth)
    {
      dep[x] = depth, fa[x] = father, sz[x] = 1;
      for (int i = h[x]; ~i; i = ne[i])
      {
        int y = e[i];
        if (y == father) continue;
        dfs1(y, x, depth + 1);
        sz[x] += sz[y];
        if (sz[son[x]] < sz[y]) son[x] = y;
      }
    }
    void dfs2(int x, int t)
    {
      id[x] = ++ tot, nw[tot] = w[x], top[x] = t;
      if (!son[x]) return;
      dfs2(son[x], t);
      for (int i = h[x]; ~i; i = ne[i])
      {
        int y = e[i];
        if (y == fa[x] || y == son[x]) continue;
        dfs2(y, y);
      }
    }
    struct Node{
      int l, r;
      ll mx, sum;
    }tr[N << 2];
    void pushup(Node &c, Node a, Node b)
    {
      c.mx = max(a.mx, b.mx);
      c.sum = a.sum + b.sum;
    }
    void pushup(int u)
    {
      pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
    }
    void build(int u, int l, int r)
    {
      if (l == r)
      {
        tr[u] = {l, l, nw[l], nw[l]};
        return;
      }
      tr[u] = {l, r};
      int mid = l + r >> 1;
      build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r);
      pushup(u);
    }
    void update(int u, int x, int c)
    {
      if (tr[u].l == x && tr[u].r == x)
      {
        tr[u] = {x, x, c, c};
        return;
      }
      int mid = tr[u].l + tr[u].r >> 1;
      if (x <= mid) update(u << 1, x, c);
      else update(u << 1 | 1, x, c);
      pushup(u);
    }
    int qmax(int u, int l, int r)
    {
      if (tr[u].l == l && tr[u].r == r) return tr[u].mx;
      int mx = -2e9;
      int mid = tr[u].l + tr[u].r >> 1;
      if (r <= mid) mx = max(mx, qmax(u << 1, l, r));
      else if (l > mid) mx = max(mx, qmax(u << 1 | 1, l, r));
      else
      {
        mx = max(mx, max(qmax(u << 1, l, mid), qmax(u << 1 | 1, mid + 1, r)));
      }
      return mx;
    }
    int qsum(int u, int l, int r)
    {
      if (tr[u].l == l && tr[u].r == r) return tr[u].sum;
      int ans = 0;
      int mid = tr[u].l + tr[u].r >> 1;
      if (r <= mid) ans += qsum(u << 1, l, r);
      else if (l > mid) ans +=qsum(u << 1 | 1, l, r);
      else
      {
        ans += qsum(u << 1, l, mid) + qsum(u << 1 | 1, mid + 1, r);
      }
      return ans;
    }
    void update_path(int x, int c)
    {
      update(1, id[x], c);
    }
    int Qmax(int u, int v)
    {
      int mx = -2e9;
      while (top[u] != top[v])
      {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        mx = max(mx, qmax(1, id[top[u]], id[u]));
        u = fa[top[u]];
      }
      if (dep[u] < dep[v]) swap(u, v);
      mx = max(mx, qmax(1, id[v], id[u]));
      return mx;
    }
    int Qsum(int u, int v)
    {
      int ans = 0;
      while (top[u] != top[v])
      {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        ans += qsum(1, id[top[u]], id[u]);
        u = fa[top[u]];
      }
      if (dep[u] < dep[v]) swap(u, v);
      ans += qsum(1, id[v], id[u]);
      return ans;
    }
    int main()
    {
      memset(h, -1, sizeof h);
      cin >> n;
      for (int i = 1; i < n; i ++ )
      {
        int a, b; scanf("%d%d", &a, &b);
        add_edges(a, b);
        add_edges(b, a);
      }
      for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
      dfs1(1, -1, 1);
      dfs2(1, 1);
      build(1, 1, n);
      cin >> m;
      char op[20];
      while (m -- )
      {
        scanf("%s", op);
        if (!strcmp(op, "CHANGE"))
        {
          int x, c; scanf("%d%d", &x, &c);
          update_path(x, c);
        }
        else if (!strcmp(op, "QMAX"))
        {
          int u, v; scanf("%d%d", &u, &v);
          printf("%d
    ", Qmax(u, v));
        }
        else
        {
          int u, v; scanf("%d%d", &u, &v);
          printf("%d
    ", Qsum(u, v));
        }
        // cout << "----------" << endl;
      }
      return 0;
    }
    

      

    P3178 [HAOI2015]树上操作

    题目:

    题目描述

    有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:

    • 操作 1 :把某个节点 x 的点权增加 a 。
    • 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
    • 操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

    输入格式

    第一行包含两个整数 N, M 。表示点数和操作数。
    接下来一行 N 个整数,表示树中节点的初始权值。
    接下来 N-1 行每行两个正整数 from, to , 表示该树中存在一条边 (from, to) 。
    再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。

    输出格式

    对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

    输入输出样例

    输入 #1
    5 5
    1 2 3 4 5
    1 2
    1 4
    2 3
    2 5
    3 3
    1 2 1
    3 5
    2 1 2
    3 3
    输出 #1
    6
    9
    13

    说明/提示

    对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。

    分析:

    这题也很好写啊,真心不知道为啥全部改成long long就可以过,而部分为long long就WA

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    // typedef long long ll;
    #define int long long 
    const int N = 100005;
    int n, m;
    int w[N], h[N], e[N << 1], ne[N << 1], idx;
    int fa[N], son[N], sz[N], dep[N];
    int id[N], nw[N], top[N], tot;
    void add_edges(int a, int b)
    {
      e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
    }
    void dfs1(int x, int father, int depth)
    {
      dep[x] = depth, fa[x] = father, sz[x] = 1;
      for (int i = h[x]; ~i; i = ne[i])
      {
        int y = e[i];
        if (y == father) continue;
        dfs1(y, x, depth + 1);
        sz[x] += sz[y];
        if (sz[son[x]] < sz[y]) son[x] = y;
      }
    }
    void dfs2(int x, int t)
    {
      id[x] = ++ tot, nw[tot] = w[x], top[x] = t;
      if (!son[x]) return;
      dfs2(son[x], t);
      for (int i = h[x]; ~i; i = ne[i])
      {
        int y = e[i];
        if (y == fa[x] || y == son[x]) continue;
        dfs2(y, y);
      }
    }
    struct Node{
      int l, r;
      int tag, sum;
    }tr[N << 2];
    void pushup(int u)
    {
      tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
    }
    void pushdown(int u)
    {
      Node &c = tr[u], &a = tr[u << 1], &b = tr[u << 1 | 1];
      if (c.tag)
      {
        a.sum += c.tag * (a.r - a.l + 1);
        b.sum += c.tag * (b.r - b.l + 1);
        a.tag += c.tag;
        b.tag += c.tag;
        c.tag = 0;
      }
    }
    void build(int u, int l, int r)
    {
      if (l == r)
      {
        tr[u] = {l, l, 0, nw[l]};
        return;
      }
      tr[u] = {l, r};
      int mid = l + r >> 1;
      build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
      pushup(u);
    }
    void update(int u, int l, int r, int c)
    {
      if (tr[u].l >= l && tr[u].r <= r)
      {
        tr[u].sum += (tr[u].r - tr[u].l + 1) * c;
        tr[u].tag += c;
        return;
      }
      pushdown(u);
      int mid = tr[u].l + tr[u].r >> 1;
      if (l <= mid) update(u << 1, l, r, c);
      if (r > mid) update(u << 1 | 1, l, r, c);
      pushup(u);
    }
    int query(int u, int l, int r)
    {
      if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
      pushdown(u);
      int res = 0;
      int mid = tr[u].l + tr[u].r >> 1;
      if (l <= mid) res += query(u << 1, l, r);
      if (r > mid) res += query(u << 1 | 1, l, r);
      return res;
    }
    void update_path(int u, int c, int tag)
    {
      if (tag == 2)
      {
        update(1, id[u], id[u] + sz[u] - 1, c);
      }
      else
      {
        update(1, id[u], id[u], c);
      }
    }
    int query_path(int u, int v)
    {
      int ans = 0;
      while (top[u] != top[v])
      {
        // if (dep[top[u]] < dep[top[v]]) swap(u, v);
        ans += query(1, id[top[u]], id[u]);
        u = fa[top[u]];
      }
      // if (dep[u] < dep[v]) swap(u, v);
      ans += query(1, id[v], id[u]);
      return ans;
    }
    signed main()
    {
      memset(h, -1, sizeof h);
      cin >> n >> m;
      for (int i = 1; i <= n; i ++ ) scanf("%lld", &w[i]);
      for (int i = 1; i < n; i ++ )
      {
        int a, b; scanf("%lld%lld", &a, &b);
        add_edges(a, b);
        add_edges(b, a);
      }
      dfs1(1, 1, 1);
      dfs2(1, 1);
      build(1, 1, n);
      while (m -- )
      {
        int p; scanf("%lld", &p);
        if (p == 1)
        {
          // printf("operation 1 :
    ");
          // for (int i = 1; i <= n * 2 - 1; i ++ ) cout << tr[i].sum << " "; cout << endl;
          int x, c; scanf("%lld%lld", &x, &c);
          update_path(x, c, 1);
          // for (int i = 1; i <= n * 2 - 1; i ++ ) cout << tr[i].sum << " "; cout << endl;
        }
        else if (p == 2)
        {
          // printf("operation 2 :
    ");
          // for (int i = 1; i <= n * 2 - 1; i ++ ) cout << tr[i].sum << " "; cout << endl;
          int x, c; scanf("%lld%lld", &x, &c);
          update_path(x, c, 2);
          // for (int i = 1; i <= n * 2 - 1; i ++ ) cout << tr[i].sum << " "; cout << endl;
        }
        else
        {
          int x; scanf("%lld", &x);
          printf("%lld
    ", query_path(x, 1));
        }
      }
      return 0;
    }
    

      

  • 相关阅读:
    day15---作业
    day14---函数的参数
    day14作业
    day--13函数的基本使用
    day13--函数练习(作业)
    day12--文件处理
    day12--作业
    Python函数对象、函数嵌套和闭包函数
    每日作业以及周末作业
    Python名称空间与作用域
  • 原文地址:https://www.cnblogs.com/Iamcookieandyou/p/15066679.html
Copyright © 2020-2023  润新知