首页 > 编程语言 >[刷题笔记] [算法学习笔记]树上差分 -- Luogu P3128

[刷题笔记] [算法学习笔记]树上差分 -- Luogu P3128

时间:2023-10-19 23:37:49浏览次数:48  
标签:fa -- Luogu 差分 贡献 int 笔记 LCA

Description

Problem:https://www.luogu.com.cn/problem/P3128
FJ 给他的牛棚的 \(N\) 个隔间之间安装了 \(N-1\) 根管道,隔间编号从 \(1\) 到 \(N\)。所有隔间都被管道连通了。
FJ 有 \(K\) 条运输牛奶的路线,第 \(i\) 条路线从隔间 \(s_i\) 运输到隔间 \(t_i\)。一条运输路线会给它的两个端点处的隔间以及中间途径的所有隔间带来一个单位的运输压力,你需要计算压力最大的隔间的压力是多少。

Analysis

前置知识:LCA

本题是树上差分的模板题,借本题我们来讲解一下树上差分。

注意到没有修改操作,每条路径 \(u-v\) 都给路径 \(u-v\) 上的每个点压力 +1,考虑树上差分。

树上差分和线性差分不是很相同,线性差分对于区间 \(a\{l,r\}\) 的每个值加 \(x\),我们只需要 \(a_r+x,a_{l-1}-x\) 即可。

回忆一下线性差分的本质,它的本质是 将区间 \(\{1,r\}\) 的每一个值 \(+x\),再将区间 \(\{1,l-1\}\) 的每一个值都 \(-x\),消除多余的影响。

对于树上差分,记 \(LCA(u,v)\) 表示 \(u,v\) 的最近公共祖先,则路径 \(u-v\) 为:\(u-LCA(u,v)-v\)

点差分和边差分还是不同的,这里先讲解本题的点差分。

为了解释方便,这里假设 \(root = 1\)。

首先将点集 \(\{1,u\}\) 上的每一个点的贡献 \(+x\),再将点集 \(\{1,v\}\) 上的每一个点 \(+x\),显然有多余的贡献。注意到 \(LCA(u,v)\) 也属于路径上的点,它只需要一次贡献,但是我们对它进行了两次计算,所以从 root到 \(LCA(u,v)\) 的贡献 \(-x\)。

其余的从 \(1\) 到 \(fa_{LCA(u,v)}\) 的点本应没有贡献,但是我们计算了两次贡献,在处理 \(LCA(u,v)\) 的时候已经消除了一次贡献,我们再对它消除一次贡献即可。也就是令 \(fa_{LCA(u,v)} - x\)。

这是对于本题的点差分而言,对于边差分,由于我们不需要考虑 \(LCA(u,v)\) 的贡献,所以我们在分别处理从 1 到 \(u,v\) 的贡献后再将从1到 \(LCA(u,v)\) 的贡献减去即可。

差分数组完成后,最后 dfs 累加即可。也就是树上前缀和操作。

这里给出一颗树供理解:
image

假设我们要求 \(6\) 号点到 \(9\) 号点上的路径,对于点差分,将 \(power_6 ++\) 也就是将从 root 到 \(6\) 号点的贡献标记为 + 1,同理对于 \(power_9 ++\) 。对于重复的贡献,\(LCA(6,9)\) 多计算了一次贡献,\(power_3--\),同时,从 root 到 \(3\) 的父节点也就是 2 及以上的点都被多统计了两次贡献,需要 --;

对于边差分,对于重复性的答案我们只需要将 \(LCA(u,v)\) 的贡献减去两次多余的贡献即可。这样就会确保我们的标记不会向 \(LCA\) 及以上延伸。

这就是树上差分的基本思想,总体来说,树上差分包括点差分和边差分,两者的处理方式有所不同。

计算 LCA 的时候为方便,倍增足够。

参考模板代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N  = 100010;
int n,m,s;
vector <int> Edge[N];
int fa[N][100];
int depth[N*2];
int power[N];
int ans = 0;
void dfs(int noww,int fat)
{
 //   cout<<noww<<endl;
    fa[noww][0] = fat;
    depth[noww] = depth[fat] + 1;
    for(int i=1;(1<<i) <= depth[noww];i++)
    {
        fa[noww][i] = fa[fa[noww][i-1]][i-1]; //递推求解
    }
    for(int i=0;i<Edge[noww].size();i++)
    {
        int v = Edge[noww][i];
        if(v != fat)
        {
            dfs(v,noww);
        }
    }
}
int lca(int a,int b)
{
    if(depth[a] > depth[b]) swap(a,b);
    for(int i=20;i>=0;i--)
    {
        if(depth[a] <= depth[b]-(1<<i)) b = fa[b][i];
    }
    if(a == b) return a;
    for(int i=20;i>=0;i--)
    {
        if(fa[a][i] == fa[b][i]) continue;
        a = fa[a][i];
        b = fa[b][i];
    }
    return fa[a][0];
}
void get(int u,int fat)
{
    for(int i=0;i<Edge[u].size();i++)
    {
        int v = Edge[u][i];
        if(v == fat) continue;
        get(v,u);
        power[u] += power[v];
    }
    ans = max(ans,power[u]);
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<n;i++)
    {
        int x,y;
        cin>>x>>y;
        Edge[x].push_back(y);
        Edge[y].push_back(x);
    }
    dfs(1,0);
    while(m--)
    {
        int a,b;
        cin>>a>>b;
        int LCA = lca(a,b);
        ++ power[a];
        ++ power[b];
        -- power[LCA];
        -- power[fa[LCA][0]];
    }
    get(1,0);
    cout<<ans<<endl;
    return 0;
}

标签:fa,--,Luogu,差分,贡献,int,笔记,LCA
From: https://www.cnblogs.com/SXqwq/p/17775972.html

相关文章

  • SpringBoot基础搭建总结
    现在这一篇就是总结springboot基本的搭建  1.这边就是Controller类,就是类名上面写一个@RestController,然后方法上面写一个@RequestMapping注解,然后就是下面方法的构建,然后下面sout的目的就是为了测试方法的运行,return就是将东西送给浏览器  然后,为了规范工作,和前端更......
  • 莫能沛--华经情报网
    1、定义洗护用品是指清洁和修饰皮肤、身体、头髮和口腔的产品。主要包括洗发露、沐浴露、洗手液、手工皂、牙膏、洗面奶等产品。2、分类洗护用品按用途可分为头部洗护用品、浴室洗护用品、清洁洗护用品等;按使用人群可分为婴儿洗护用品、成人洗护用品、孕妇洗护用品等。  ......
  • 【专题】2022年中国制造业数字化转型研究报告PDF合集分享(附原数据表)
    报告链接:http://tecdat.cn/?p=32145本文中所说的制造业数字化转型,指的是在制造企业的设计、生产、管理、销售及服务的每一个环节中,将新一代信息技术应用到制造企业的设计、生产、管理、销售及服务的每一个环节中,并可以以每一个环节中产生的数据为基础,展开控制、监测、检测、预测......
  • 大数据mapReduce的学习
    .2MapReduce模型简介•MapReduce将复杂的、运行于大规模集群上的并行计算过程高度地抽象到了两个函数:Map和Reduce•编程容易,不需要掌握分布式并行编程细节,也可以很容易把自己的程序运行在分布式系统上,完成海量数据的计算•MapReduce采用“分而治之”策略,一个存储在分布式文件系......
  • 实验1 类和对象
    实验任务一:1#include<iostream>2#include<string>3#include<vector>4#include<array>5template<typenameT>6voidoutput1(constT&obj)7{8for(autoi:obj)9std::cout<<i<<",";......
  • git提交到远程库如何退回呢?
    找到提交到远程库的编号xx在ideaterminal中使用以下命令,退回到指定的版本号gitreset--soft编号xxx提示:--soft会保留指定版本下的代码到暂存区--hard指定版本本地库下的代码会丢失然后使用以下命令强制推送到远程库,注意强制推送会导致指定版本后的远程库代码会......
  • 每日总结
    今日收获被骂了一顿,将erp系统的流程重新梳理了一下,然后重新搭建了相关的页面网站;趁着这几天将C#的大作业写完先;做了部分的人机交互大作业;学习操作系统相关知识;明天预计学习操作系统相关知识;做C#大作业;做人机交互大作业;打比赛;......
  • 操作系统之段页式存储组织
    1、例题展示2、例题解决......
  • 【专题】2022年智慧城市白皮书报告PDF合集分享(附原数据表)
    报告链接:http://tecdat.cn/?p=32732本白皮书对智慧城市的发展历程进行了归纳和总结,分析了发展实践中的新变化和新内涵,并提出了一系列新的智慧城市建设理念、架构和建议。阅读原文,获取专题报告合集全文,解锁文末29份智慧城市相关行业研究报告。其目的在于为建设新型智慧城市提供......
  • 线程
    2023.10.191.在java中线程是有分优先等级的,可以用setPriority()设置2.Thread实现了Runnable接口是一个类不是接口3.实现多线程的三种方式,一种是继承Thread类使用此方式就不能继承其他的类了。还有两种是实现Runnable接口或者实现Callable接口......