题意:

给出一棵\(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. java 多线程死锁

    死锁案例: package com.test; public class DealThread implements Runnable { public String username; public ...

  2. leetcode84 Largest Rectangle in Histogram

    思路: 使用单调栈计算每个位置左边第一个比它矮的位置和右边第一个比它矮的位置即可. 实现: #include <bits/stdc++.h> using namespace std; cl ...

  3. JFinal教程:JFinal极速开发企业实战百集JFinal视频教程发布

    课程名称:JFinal极速开发企业实战 课程长度:100课时 课程作者:小木(909854136) 课程地址:http://edu.csdn.net/course/detail/1968 官网网址:h ...

  4. gitinore修改不生效

    .gitignore只能忽略那些尚未被被track的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的.一个简单的解决方法就是先把本地缓存删除(改变成未track状态),然后 ...

  5. 【虚拟机-网络IP】保留正在使用的 VIP

    本文包含以下内容 适用场景 操作步骤 保留IP的费用 适用场景 我们希望云服务有一个固定的 IP,即使虚拟机以 deallocated 的方式关闭. 用户忘记先保留 VIP 而直接完成了部署. 希望不 ...

  6. Linux之bash shell的学习

    1.什么是bash  shell bash 是Bourne Again Shell的简称,是从unix系统中的sh发展而来,是用户和偶Linux内核交互的工具,用户通过bash操作内核完成系统的使用和 ...

  7. GWT-2.5.1离线安装

    GWT官方离线包下载地址 http://dl.google.com/eclipse/plugin/3.7/zips/gpe-e37-latest-updatesite.zip 以下是GWTDesign ...

  8. [神经网络]一步一步使用Mobile-Net完成视觉识别(四)

    1.环境配置 2.数据集获取 3.训练集获取 4.训练 5.调用测试训练结果 6.代码讲解 本文是第四篇,下载预训练模型并训练自己的数据集. 前面我们配置好了labelmap,下面我们开始下载训练好的 ...

  9. 监控服务端口状态python脚本

    #!/usr/bin/python import socket,os,time data={ 8080:"tomcat9", 18080:"tomcat_hjgdmj&q ...

  10. 使用ServiceController组件控制计算机服务

    实现效果: 知识运用: ServiceController组件的MachineName属性 //获取或设置服务所驻留的计算机名称 public string MachineName{get;set;} ...