zoukankan      html  css  js  c++  java
  • HDU 5044 Tree LCA

    题意:

    给出一棵(n(1 leq n leq 10^5))个节点的树,每条边和每个点都有一个权值,初始所有权值为0。
    有两种操作:

    • (ADD1 \, u \, v \, k):将路径(u o v)上所有节点的权值都加上(k)
    • (ADD2 \, u \, v \, k):将路径(u o v)上所有边的权值都加上(k)

    最后输出每个点和边的权值。

    分析:

    看到树上成段更新,第一反应就是树链剖分,然而这样的算法并不能通过这道题目的数据。
    注意到这道题的特殊之处在于多次操作但只有一次查询。

    考虑序列上的这样一个问题:

    • 有一个序列(A)和有若干次操作,每次让区间([l,r])的元素加上一个值(k),最后输出每个元素最终的权值。
      维护一个序列(B),使得序列(A)是序列(B)的前缀和。
      因此,如果将(B_l)的权值增加(k),那么相当于将(A_l sim A_n)的权值增加(k)
      再将(B_{r+1})的权值减去(k),相当于将(A_{r+1} sim A_n)的权值减去(k)
      最终的效果就是将(A_l sim A_r)的权值增加的(k),其他位置权值不变。

    所以,这样就在(O(1))的时间完成了成段更新。

    回到本题上来:受上面思路的启发,我们也可以在某些个点处修改,最后从叶子节点到根节点求一个前缀和得到最终答案。

    具体来说就是:

    • 对于点权值的修改:(B_u, \, B_v)的权值增加(k)(B_{lca}, \, B_{fa(lca)})的权值减少(k)
    • 对于边权值的修改:(B_u, \, B_v)的权值增加(k)(B_{lca})的权值减少(2k)
      其中(lca)(u,v)的最近公共祖先。

    最后还有一个需要注意的地方就是(n=1)的情况,输出边权值只需要输出一个空行即可。
    也许有更巧妙的写法可以避免这个问题。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    typedef long long LL;
    const int maxn = 100000 + 10;
    
    struct Edge
    {
        int v, nxt;
    	Edge() {}
    	Edge(int v, int nxt): v(v), nxt(nxt) {}
    };
    
    int ecnt, head[maxn];
    Edge edges[maxn * 2];
    
    void AddEdge(int u, int v) {
        edges[ecnt] = Edge(v, head[u]);
        head[u] = ecnt++;
    }
    
    int n, m;
    int u[maxn],v[maxn];
    int fa[maxn], dep[maxn];
    
    LL sum1[maxn], sum2[maxn];
    
    void dfs(int u) {
        for(int i = head[u]; ~i; i = edges[i].nxt) {
            int v = edges[i].v;
            if(v == fa[u]) continue;
            fa[v] = u;
            dep[v] = dep[u] + 1;
            dfs(v);
        }
    }
    
    int anc[maxn][20];
    
    void preprocess() {
        memset(anc, 0, sizeof(anc));
        for(int i = 1; i <= n; i++) anc[i][0] = fa[i];
        for(int j = 1; (1 << j) < n; j++)
            for(int i = 1; i <= n; i++) if(anc[i][j-1])
                anc[i][j] = anc[anc[i][j-1]][j-1];
    }
    
    int LCA(int u, int v) {
        if(dep[u] < dep[v]) swap(u, v);
        int log;
        for(log = 0; (1 << log) < dep[u]; log++);
        for(int i = log; i >= 0; i--)
            if(dep[u] - (1<<i) >= dep[v])
                u = anc[u][i];
        if(u == v) return u;
        for(int i = log; i >= 0; i--)
            if(anc[u][i] && anc[u][i] != anc[v][i])
                u = anc[u][i], v = anc[v][i];
        return fa[u];
    }
    
    void dfs2(int u) {
        for(int i = head[u]; ~i; i = edges[i].nxt) {
            int v = edges[i].v;
            if(v == fa[u]) continue;
            dfs2(v);
            sum1[u] += sum1[v];
            sum2[u] += sum2[v];
        }
    }
    
    int main()
    {
        int T; scanf("%d", &T);
        for(int kase = 1; kase <= T; kase++) {
            scanf("%d%d", &n, &m);
            
            ecnt = 0;
    		memset(fa, 0, sizeof(fa));
    		memset(dep, 0, sizeof(dep));
            memset(head, -1, sizeof(head));
            for(int i = 1; i < n; i++) {
                scanf("%d%d", u + i, v + i);
                AddEdge(u[i], v[i]);
                AddEdge(v[i], u[i]);
            }
            dfs(1);
            preprocess();
            
            memset(sum1, 0, sizeof(sum1));
            memset(sum2, 0, sizeof(sum2));
            while(m--) {
                char op[10]; int a, b, k;
                scanf("%s", op);
                scanf("%d%d%d", &a, &b, &k);
                int lca = LCA(a, b);
                if(op[3] == '1') {
                    sum1[a] += k;
                    sum1[b] += k;
                    sum1[lca] -= k;
                    if(lca != 1) sum1[fa[lca]] -= k;
                } else {
                    sum2[a] += k;
                    sum2[b] += k;
                    sum2[lca] -= k * 2;
                }
            }
            
            dfs2(1);
            for(int i = 1; i < n; i++)
                if(dep[u[i]] < dep[v[i]])
                    swap(u[i], v[i]);
            
            printf("Case #%d:
    ", kase);
            for(int i = 1; i < n; i++) printf("%lld ", sum1[i]);
            printf("%lld
    ", sum1[n]);
    		if(n == 1) { puts(""); continue; }
            for(int i = 1; i < n - 1; i++)
                printf("%lld ", sum2[u[i]]);
            printf("%lld
    ", sum2[u[n-1]]);
        }
        
        return 0;
    } 
    
  • 相关阅读:
    WINDOWS操作系统下OpenERP源码运行的环境:eclipse + pydev + python2.7
    12款优秀的 Twitter Bootstrap 组件和工具
    推荐电脑上好用的但相对冷门的软件
    财务记账演练
    OpenERP Custom Sample Module Development – OpenERP Quick Start Guide
    免费编程入门教程资源推荐搜集
    50个很棒的Python模块
    kindle 资源
    怎么看内存的类型?
    计算机必懂的53个英文单词和缩写
  • 原文地址:https://www.cnblogs.com/AOQNRMGYXLMV/p/5330519.html
Copyright © 2011-2022 走看看