题意:

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

有两种操作:

  • \(ADD1 \, u \, v \, k\):将路径\(u \to v\)上所有节点的权值都加上\(k\)
  • \(ADD2 \, u \, v \, k\):将路径\(u \to 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:\n", kase);
for(int i = 1; i < n; i++) printf("%lld ", sum1[i]);
printf("%lld\n", sum1[n]);
if(n == 1) { puts(""); continue; }
for(int i = 1; i < n - 1; i++)
printf("%lld ", sum2[u[i]]);
printf("%lld\n", sum2[u[n-1]]);
} return 0;
}

HDU 5044 Tree LCA的更多相关文章

  1. HDU 5044 Tree(树链剖分)

    HDU 5044 Tree field=problem&key=2014+ACM%2FICPC+Asia+Regional+Shanghai+Online&source=1&s ...

  2. HDU 5044 离线LCA算法

    昨天写了HDU 3966 ,本来这道题是很好解得,结果我想用离线LCA 耍一把,结果发现离线LCA 没理解透,错了好多遍,终得AC ,这题比起 HDU 3966要简单,因为他不用动态查询.但是我还是错 ...

  3. HDU 5044 Tree 树链剖分+区间标记

    Tree Problem Description You are given a tree (an acyclic undirected connected graph) with N nodes. ...

  4. HDU 5044 Tree --树链剖分

    题意:给一棵树,两种操作: ADD1: 给u-v路径上所有点加上值k, ADD2:给u-v路径上所有边加上k,初始值都为0,问最后每个点和每条边的值,输出. 解法:树链剖分可做,剖出来如果直接用线段树 ...

  5. HDU 5044 TREE

    题意:一棵树上两种操作,操作1,改变u到v的每一点的值增加k,操作2,改变u到v每一条边值增加k.最后结束时问,每一点和每一条边的值. 初始时,点和边的值都为0. 分析: 很显然要用树链剖分,将点和边 ...

  6. hdu 5274 Dylans loves tree(LCA + 线段树)

    Dylans loves tree Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Othe ...

  7. [HDU 5293]Tree chain problem(树形dp+树链剖分)

    [HDU 5293]Tree chain problem(树形dp+树链剖分) 题面 在一棵树中,给出若干条链和链的权值,求选取不相交的链使得权值和最大. 分析 考虑树形dp,dp[x]表示以x为子树 ...

  8. hdu 5909 Tree Cutting [树形DP fwt]

    hdu 5909 Tree Cutting 题意:一颗无根树,每个点有权值,连通子树的权值为异或和,求异或和为[0,m)的方案数 \(f[i][j]\)表示子树i中经过i的连通子树异或和为j的方案数 ...

  9. HDU 4757 Tree(可持久化字典树)(2013 ACM/ICPC Asia Regional Nanjing Online)

    Problem Description   Zero and One are good friends who always have fun with each other. This time, ...

随机推荐

  1. C#/.net基础知识

    1 .NET  中类和结构的区别? 答:结构和类具有大体的语法,但是结构受到的限制比类要多.结构不能申明有默认的构造函数,为结构的副本是又编译器创建 和销毁的,所以不需要默认的构造函数和析构函数.结构 ...

  2. 在docker上centos7 编译安装php7

    docker镜像来自daocloud.io/library/centos 首先下载libmcrypt库并make && make install yum -y install gcc ...

  3. 百度地图web 笔记

    1.marker点击事件获取marker的title和lebal等信息 marker.setTitle(title); marker.setLabel(label); marker.addEventL ...

  4. MFC制作简单通讯录程序

    学习c++和MFC一段时间了,苦于没有项目实战,所以自己写了一个简单的简单通讯录程序,以前用c#写简单很多,例程是这本书上的实例,我的第一个winform程序也是从这本书上学的,总结c#写的话更简单, ...

  5. [Java]Eclipse无法import “com.sun.management.OperatingSystemMXBean”的解决方法

    背景: 当使用jdk的自带的API获取系统信息时,代码中import “com.sun.management.OperatingSystemMXBean”出现报错. 解决方法: Eclipse默认把这 ...

  6. javaSe-反射3

    package com.java.chap07.sec04; public class Student { private String name; private Integer age; publ ...

  7. BZOJ 1806: [Ioi2007]Miners 矿工配餐

    ime Limit: 10 Sec  Memory Limit: 64 MBSubmit: 910  Solved: 559[Submit][Status][Discuss] Description ...

  8. SAP公有云和私有云解决方案概述

    SAP公有云解决方案见下图最右侧,比较著名的有SAP SuccessFactors和SAP Cloud for Customer(C4C)等,作为SAP软件即服务(SaaS)的解决方案. 而最左侧的S ...

  9. caffe parse_log.sh

    画loss曲线需要用到此shell脚本 #!/bin/bash # Usage parse_log.sh caffe.log # It creates the following two text f ...

  10. python_105_类的特殊成员方法

    aa.py class C(): def __init__(self): self.name='QiZhiguang' 类的特殊成员方法: # 1. __doc__ 表示类的描述信息 class Do ...