题意:

给出一棵\(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. SQL 索引查找

    索引查找信息 在非聚集索引里,会为每条记录存储一份非聚集索引索引键的值和一份聚集索引索引键 [在没有聚集索引的表格里,是RID值指向数据页面,有聚集索引的话指向聚集索引的键(在不使用include时) ...

  2. vue学习之路之需要了解的知识汇总

    一.vue是什么? 相关网页:  https://vuejs.bootcss.com/v2/guide/       及菜鸟教程       https://www.runoob.com/vue2/v ...

  3. Jsp动态生成表格

    输入行列: <body> <form action="Train2ResultJsp.jsp"> row:<input type="text ...

  4. 零基础逆向工程21_PE结构05_数据目录表_导出表

    数据目录 1.我们所了解的PE分为头和节,在每个节中,都包含了我们写的一些代码和数据,但还有一些非常重要 的信息是编译器替我们加到PE文件中的,这些信息可能存在在任何可以利用的地方. 2.这些信息之所 ...

  5. Godaddy虚拟主机新建mysql数据库 2019最新

    第一次用狗爹,完全摸不着路子. 网站本地已搭建,不知道数据库是在哪里上传. 百度搜索结果都是四五年前的旧内容,耽误时间. 还是问客服,Godaddy的客服确实不赖 godaddy虚拟主机如何新建数据库 ...

  6. nmap -sS

    SYN 扫描,半连接,受到syn/ack响应后意味着端口开放,收到rst包意味着端口关闭.

  7. 【虚拟机-可用性集】ARM 中可用性集使用的注意事项

    Azure 目前有两种部署模型:经典部署模型 (ASM) 和资源管理器 (ARM).如果您之前使用过 ASM 模式下的可用性集,那么很可能在使用 ARM 模式下的可用性集时,会遇到一些问题或者疑惑.这 ...

  8. python+selenium之验证码的处理

    对于web应用来说,大部分的系统在用户登录时都要求用户输入验证码.验证码的类型很多,有字母数字的,有汉字的.甚至还有需要用户输入一道算术题的答案的.对于系统来说,使用验证码可以有效地防止采用机器猜测方 ...

  9. 洛谷 P2598 [ZJOI2009]狼和羊的故事

    题目描述 “狼爱上羊啊爱的疯狂,谁让他们真爱了一场:狼爱上羊啊并不荒唐,他们说有爱就有方向......” Orez听到这首歌,心想:狼和羊如此和谐,为什么不尝试羊狼合养呢?说干就干! Orez的羊狼圈 ...

  10. HTTP协议初探

    HTTP协议初探 HTTP协议初探 什么是http协议? 遵守协议的双方 再来回答什么是http协议 抓到这两段文本 可以总结出以下规律 HTTP 请求命令(动作,谓词 ,METHOD) GET 和 ...