题意:

给出一棵\(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. SpriingMVC执行流程结构

    SpringMVC也叫spring web mvc,属于表现层的框架,是Spring框架的一部分. Spring  MVC请求流程图: request-------->DispatcherSer ...

  2. 开源组件 Mark

    http://www.cnblogs.com/asxinyu/category/661170.html

  3. 关于dataTable 生成JSON 树

    背景: POSTGRESL + C#  + DHTMLX SUIT 一个表生成一个JSON串,这个不是很麻烦: 1.在数据库(postges)中:  json_agg(row_to_json(t)) ...

  4. windows下安装pm2

    安装pm2 npm install pm2 -g 添加系统环境变量 PM2_HOME=C:\Users\PCONE\.pm2 打开新的cmd命令行窗口,执行以下命令来安装服务 pm2-service- ...

  5. [nmon]使用nmon工具监控系统资源

    1.下载nmon 下载正确的nmon版本, 查看linux服务器版本,命令:lsb_release -a,查看到当前系统为RedHat 6.4 然后我们根据我们的linux版本,下载相应nmon版本, ...

  6. 【Python图像特征的音乐序列生成】关于图像特征的描述词

    查阅了很久的资料,决定依据Yoshida的<Image retrieval system using impression words>这篇论文里的词语来定义. Yoshida 等的 Ar ...

  7. java面试题(杨晓峰)---第二讲Exception和Error有什么区别?

    本人总结: Exception和Error:正常问题和意外问题,以自行车举例:没气和爆胎. ①理解Throwable,Exception,Error的设计和分类. ②掌握哪些应用最广泛的子类, ③如何 ...

  8. linux概念和体系

    1. Linux开机启动 2. Linux文件管理 3. Linux的架构 4. Linux命令行与命令 5. Linux文件管理相关命令 6. Linux文本流 7. Linux进程基础 8. Li ...

  9. 学习Unity 4.6新GUI系统

    (搬运自我在SegmentFault的博客) 最近在学习Unity的过程中,自己做一款小游戏自娱自乐.自然需要用到GUI.但4.5中的GUI很难用,一个选择是传说中的NGUI插件.但对于4.6中的新G ...

  10. [学习笔记] C++ 历年试题解析(二)--程序题

    发现程序题也挺有价值的. 顺便记录下来几道. 1.题目 #include <iostream> #include <cstring> using namespace ① std ...