zoukankan      html  css  js  c++  java
  • 数据结构题锦——树链

    树链:

    树链求最近公共祖先

    最近公共祖先

    题目:

    题目描述

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

    输入格式

    第一行包含三个正整数 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;
    }
    

      

  • 相关阅读:
    Chrome开发者工具中Elements(元素)断点的用途
    最简单的SAP云平台开发教程
    Java实现 LeetCode 495 提莫攻击
    Java实现 LeetCode 494 目标和
    Java实现 LeetCode 494 目标和
    Java实现 LeetCode 494 目标和
    Java实现 LeetCode 493 翻转对
    Java实现 LeetCode 493 翻转对
    Java实现 LeetCode 493 翻转对
    Java实现 LeetCode 492 构造矩形
  • 原文地址:https://www.cnblogs.com/Iamcookieandyou/p/15066679.html
Copyright © 2011-2022 走看看