首页 > 其他分享 >点分治 学习笔记

点分治 学习笔记

时间:2024-06-07 16:35:05浏览次数:15  
标签:le cur int siz 分治 笔记 学习 maxn dfa

引入

在点分治的过程中,它的遍历顺序会遍历每棵子树的重心,而这棵由重心生成的树会产生一棵新的树,便是点分树。

常用来解决树上与树的形态无关的路径问题。

过程

如下图,它的点分树是它自己。


因此可以看出一棵树的点分树可能是它本身。

性质

  1. 因为点分树 \(\mathcal{O}(\log n)\) 因此很显然的是点分树树高也是 \(\mathcal{O}(\log n)\) 的。
  2. 树上两点的 \(LCA\) 必定在原树两点路径上。

根据第一条性质,点分树就可以解决一些复杂度瓶颈在树的形态上的且问题与树的形态无关的题。

例题

【模板】点分治 | 震波

简要题意

给定 \(n(1 \le n \le 10 ^ 5)\) 个点的树,每个点 \(i\) 点权为 \(w_i (0 \le w_i \le 10^4)\)和 \(m(1 \le m \le 10^5)\) 次操作。

每次操作有修改或查询。

修改操作,将第 \(i\) 个点的点权改为 \(v(1 \le v \le 10^4)\);
查询操作,查询距离 \(i\) 距离小于等于 \(k(0 \le k \le n - 1)\) 的点的点权和。

思路

点分治模板题,建立出点分树后,只需要处理出每个点的子树中距离它为 \(k\) 的点的点权和和它的父亲距离它的子树中距离为 \(k\) 的点权和。

之后修改就暴力跳父亲,用树状数组修改;查询也暴力跳父亲查询即可。

代码
#include <cstdio>
#include <vector>
#include <bitset>
#include <queue>
#include <algorithm>
#include <iostream>

using u32 = unsigned int ;
using i64 = long long ;
using u64 = unsigned long long ;

const int N = 1e5 + 5 ;

struct FenwickTree{
    std::vector<int> t;
    u32 siz;

    void resize(const int & n){
        siz = n + 5;
        t.resize(n + 10, 0);
    }

    int lowbit(const int & x){
        return x & -x;
    }
    
    void modify(u32 x, int val){
        ++x;
        for (; x <= siz; x += lowbit(x)) {
            t[x] += val;
        }
    }
    
    int query(u32 x){
        int ans = 0;
        
        ++x;
        x = std::min(x, siz);
        for (; x; x -= lowbit(x)) {
            ans += t[x];
        }

        return ans;
    }
}t[2][N];
// t[0][i] 第 i 个点的子树距离它小于等于 k 的权值和, t[1][i] 第 i 个点的父亲距离它子树小于等于 k 的权值和

int n, m;
int val[N];

std::vector<int> g[N];
int dfa[N];

bool vis[N];

int siz[N], rt;
void GetRoot(int u, int FA, int sum){
    siz[u] = 1;
    int maxn = 0;

    for (auto v : g[u]) {
        if (v != FA && !vis[v]) {
            GetRoot(v, u, sum);
            siz[u] += siz[v];
            maxn = std::max(maxn, siz[v]);
        }
    }

    maxn = std::max(maxn, sum - siz[u]);
    if (maxn * 2 <= sum) {
        rt = u;
    }
}

// ----------------------------- 树链剖分
int fa[N], dep[N];
int son[N];
void dfs1(int u, int FA){
    siz[u] = 1;
    fa[u] = FA;
    dep[u] = dep[FA] + 1;

    for (auto v : g[u]) {
        if (v != FA) {
            dfs1(v, u);
            siz[u] += siz[v];
            if (siz[son[u]] < siz[v]) {
                son[u] = v;
            }
        }
    }
}

int top[N];
void dfs2(int u, int x){
    top[u] = x;

    if (!son[u]) {
        return;
    }

    dfs2(son[u], x);

    for (auto v : g[u]) {
        if (v != son[u] && v != fa[u]) {
            dfs2(v, v);
        }
    }
}
// ---------------------------- end

void dfs(int u){
    vis[u] = true;

    t[0][u].resize(siz[u]);

    for (auto v : g[u]) {
        t[1][v].resize(siz[v]);
    }

    for (auto v : g[u]) {
        if (!vis[v]) {
            GetRoot(v, u, siz[v]);
            GetRoot(rt, 0, siz[v]);

            dfa[rt] = u;
            dfs(rt);
        }
    }
}

int LCA(int x, int y){
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) {
            std::swap(x, y);
        }
        x = fa[top[x]];
    }
    return dep[x] < dep[y]? x: y;
}

int dist(int x, int y){
    return dep[x] + dep[y] - (dep[LCA(x, y)] << 1);
}

int query(int x, int k){
    int res = t[0][x].query(k);
    int cur = x;

    while (dfa[cur]) {
        int d = dist(dfa[cur], x);
        if (d > k) {
            cur = dfa[cur];
            continue;
        }

        res += t[0][dfa[cur]].query(k - d);
        res -= t[1][cur].query(k - d);

        cur = dfa[cur];
    }

    return res;
}

void modify(int x, int v){
    int cur = x;
    
    while (cur) {
        t[0][cur].modify(dist(x, cur), v);
        if (dfa[cur]) {
            t[1][cur].modify(dist(x, dfa[cur]), v);
        }

        cur = dfa[cur];
    }
}

int main(){
    scanf("%d %d", &n, &m);

    for (int i = 1; i <= n; i++) {
        scanf("%d", val + i);
    }

    for (int i = 1; i < n; i++) {
        int u, v;
        scanf("%d %d", &u, &v);

        g[u].push_back(v);
        g[v].push_back(u);
    }

    dfs1(1, 0);
    dfs2(1, 1);

    GetRoot(1, 0, n);
    GetRoot(rt, 0, n);
    dfs(rt);

    for (int i = 1; i <= n; i++) {
        modify(i, val[i]);
    }

    int lst = 0;
    for (int i = 1; i <= m; i++) {
        int op, x, y;
        scanf("%d %d %d", &op, &x, &y);

        x ^= lst, y ^= lst;

        if (op == 0) {
            printf("%d\n", lst = query(x, y));
        } else {
            modify(x, y - val[x]);
            val[x] = y;
        }
    }
    return 0;
}

标签:le,cur,int,siz,分治,笔记,学习,maxn,dfa
From: https://www.cnblogs.com/zdrj/p/18237427

相关文章

  • 不凡学院笔记
    Vue前端性能难题Vue前端性能问题通常涉及到如何优化组件渲染性能,减少不必要的DOM更新,以及处理大量数据的渲染和滚动性能。以下是一些常见的Vue前端性能问题及其解决方案:避免在v-for循环中使用key,除非你明确知道元素的交互方式。解决方案:使用唯一且稳定的ID作为key。避免在模板......
  • 得帆云学习笔记
    数仓规划数仓规划是开发人员对业务的解析、分类和提炼的过程。数仓开发人员需要根据对整体业务的理解来划分出不同业务领域、业务领域下对应的数据域、以及数据域下的业务过程。根据业务的类型或其他特征来划分业务领域。根据该业务下再细分出的类别来划分数据域。根据业务中......
  • 学习前端3DThreejs一篇就够了,从入门到实战
    vue安装three.jsnpminstall--savethree引入three.jsimport*asTHREEfrom'three'three.js结构### three.js坐标创建一个场景scene场景,camera相机,renderer渲染器创建一个场景this.scene=newTHREE.Scene()创建一个透视摄像机this.camera=newTHR......
  • 推荐系统三十六式学习笔记:原理篇.内容推荐07|人以群分,你是什么人就看到什么世界
    目录协同过滤基于用户的协同过滤背后的思想原理实践1、构造矩阵2、相似度计算3、推荐计算4、一些改进应用场景:总结谈及推荐系统,不得不说大名鼎鼎的协同过滤。协同过滤的重点在于协同,所谓协同,也就是群体互帮互助,互相支持是群体智慧的体现,协同过滤也是这般简单直接,历......
  • 简单的模型训练学习
    一、操作流程加载数据集数据预处理:将输入输出按特定格式拼接文本转TokenIDs通过labels标识出哪部分是输出(只有输出的token参与loss计算)加载模型、Tokenizer定义数据规整器定义训练超参:学习率、批次大小、...定义训练器开始训练注意:训练后推理时,输入数据的拼接方......
  • Diffusers代码学习: IP-Adapter(续)
    但是IP-Adapter不仅可以通过文生图的方式,也可以通过图生图的方式生成目标图片,就无需使用提示词。只不过同上一篇所述,底层的逻辑和图生图是完全不同的。# 以下代码为程序运行进行设置,使用图生图的自动管道,importosos.environ["HF_ENDPOINT"]="https://hf-mirror.com" ......
  • k8s学习--ingress详细解释与应用(nginx ingress controller))
    文章目录lngress简介什么是IngressIngress的用途Ingress的工作原理Ingress的工作流程Ingress的应用场景应用实验环境部署nginxingresscontroller1.安装metalLB2.nginxingresscontroller部署3.ingress对象应用案例(基于名称的负载均衡)(1)创建deployment控制......
  • AI 绘画零基础如何学习?AIGC绘画设计入门教学
    AI作画入门到是不难,有手就行。我们先从最简单的开始。完成这件事,只有一个步骤:找到一个能画画的AI工具,输入动机。这个工具叫做DiscoDiffusion。它只认识英文,不过这不是问题,你找个翻译软件把中文翻译成英文就行。如果你会科学上网,那么你打开这个网址,点击里面的"openincola......
  • Linux磁盘管理-LVM入门学习建议
    Linux磁盘管理-LVM入门学习建议准确掌握基础概念基础概念非常重要,以LVM逻辑卷为例,必须熟练掌握LV、PV以及VG的基本概念。之后才能进行更为复杂的管理操作。LVM基本大纲这里罗列出了学习LVM入门的基本大纲,供大家参考......
  • 读书笔记分享
    1.绝大多数父母都是爱孩子的,可他们却不是称职的父母。世界上任何职业都要培训、考核、竞争上岗,唯有“父母”这个职业是没有这些程序,只要生了小孩,就是天经地义的父母。2.由于自身工作特点,“白领”们的部分器官和组织,如脑组织、视觉神经、颈椎等经常处于过度紧张状态,如果不......