分类 算法竞赛 下的文章

树链剖分

重链剖分

树链剖分:将树分割成若干条链的形式,用于维护树上的信息。

形式:

  • 重链剖分
  • 长链剖分
  • 实链剖分
  • 等等

重链剖分由于将树上数据的维护变成了序列数据的维护,因此可以用线段树等维护序列的数据结构便捷地维护。

定义申明

  1. 重儿子(Heavy Son):对于一个节点 u,其所有子节点中子树大小最大的那个子节点称为重儿子。如果有多个最大,任意选一个。
  2. 轻儿子(Light Son):除了重儿子以外的其他子节点。
  3. 重边(Heavy Edge):连接节点 u 和其重儿子的边。
  4. 轻边(Light Edge):连接节点 u 和其轻儿子的边。
  5. 重链(Heavy Chain):由一系列重边连接而成的路径。重链的开头是轻儿子或根节点。

需要为每个节点 u 存储的信息:

  • father[u]:节点 u 的父节点。
  • deep[u]:节点 u 的深度(根节点深度为0或1)。
  • sz[u]:以 u 为根的子树的节点数量。
  • son[u]:节点 u 的重儿子。
  • top[u]:节点 u 所在重链的顶端节点。
  • tid[u](或 id[u]):节点 uDFS 序(新编号) 中的位置。
  • wt[dfn[u]]:DFS 序对应位置的节点权值。

把落单的结点也当作重链,那么整棵树就被剖分成若干条重链。如图:

树

来源于OI-WiKi,侵删

注意:

节点编号有:

  • 原编号(节点ID)
    • 范围:1 到 n
    • 用途x, fa, edge[i].to 等都使用这个编号体系
    • 示例:根节点是1,它的子节点可能是2,3,4等
  • DFS序编号 (tid)
    • 范围:1 到 n
    • 用途:线段树中的位置编号
    • 映射tid[x] 表示原节点x的DFS序位置
  • 边的索引 (edge下标)
    • 范围:1 到 cnt(边的计数器)
    • 用途:邻接表中遍历边时使用

实现

共用两个DFS:

  1. 信息收集

    目的: 计算每个节点的基础信息,为后续剖分做准备。

    获得了以下基础信息:

    • 每棵树的子树大小
    • 标记父节点和深度
    • 确定重儿子

    具体步骤:

    void dfs1(int x, int fa, int depth) // 节点id,fa id,节点在树上的深度;
    {
        sz[x] = 1; // 初始化节点子树大小
        father[x] = fa;
        deep[x] = depth; // 节点在树上的深度
    
       	// 遍历该节点的所有子节点(链式前向星)
        for (int i = head[x]; i; i = edge[i].next) // i是指针
        {
            Node v = edge[i].to;
            if (v == fa) continue; // 非有向边
    
            dfs1(v, x, depth+1);
            sz[x] += sz[v];
    
            // 判断重儿子 <- 没有重儿子/新节点更重
            if (!son[x] or sz[v] > deep[son[x]]) 
                son[x] = v;
        }
    }
    
  2. 划分重链并生成DFS序

    核心逻辑:

    1. DFS序分配 (tid[])
      • 每个节点被访问时获得一个唯一的DFS序编号
      • 关键技巧:先递归重儿子,保证同一条重链上的节点编号连续
    2. 重链划分 (top[])
      • tp 参数表示当前重链的顶端节点
      • 重儿子继承父亲的链顶 (dfs2(son[x], tp))
      • 轻儿子自己成为新链的顶端 (dfs2(轻儿子, 轻儿子))

    具体步骤:

    void dfs2(int x, int tp)
    {
        tid[x] = ++tidnum;
        pos[tid[x]] = x; // 反向映射,用于通过DFS序来查找节点
        top[x] = tp; // 标记链顶
    
        // 叶子节点特判
        if (!son[x]) return;
        dfs2(son[x], tp); // 先对重儿子进行DFS保证DFS序的顺序
    
        // 对轻节点进行遍历:
        // - 遍历所有节点
        // - 判断是否是重节点:
        // ├── 不是:DFS并以它本身作为链顶
        // └── 是:跳过
        for (int i = head[x]; i; i = edge[x].next) 
        {
            int v = edge[i].to;
            if (v != son[x] and v != father[x]) // 注意:无向边需特判父节点
            {
               	dfs2(v, v);
            }
        }
    }
    

附:链式前行星代码

// 链式前向星
struct Node {
    int to, next;
//  int w; //权重
} edge[maxn << 2]; // 边翻四倍

addedge(int u, int v) 
{
    edge[++cnt] = {v, head[u]};
    head[u] = cnt;
}

计算机的构成

作者为蒟蒻高中牲,若有不妥之处请多多包含,并望于评论区指正,不胜感激!

目录:

计算机由软件硬件构成。

- 硬件是指看得见摸得着的各种电子元器件。如主机、键盘、鼠标等。 - 软件看不见摸不着,但在逻辑上,我们确实可以感知到它的存在,它由人们事先编制的具有某种功能的程序组成。比如操作系统、编程语言等。

计算机的软件通常又分为:系统软件和应用软件

  • 系统软件:又称为系统程序,主要用来管理整个计算机系统,监视服务,使系统资源得到合理调度。它包括:操作系统、语言处理程序、数据库管理系统、网络软件、服务程序等。
  • 应用软件:又称为应用程序,它是用户根据需要事先编制的程序。比如 QQ、微信等。

硬件系统

在冯 · 诺依曼体系中,计算机硬件系统是由运算器、控制器、存储器、输入设备和输出设备五大部件组成的。

随着计算机技术的发展,计算机硬件系统的组织结构发生了许多重大的变化,如运算器控制器已组合成一个整体,称为中央处理器(Central Processing Unit,CPU)。

存储器已成为多级存储器体系,包含主存、高速缓存和外存三个层次。

软件系统

软件类别:系统软件应用软件

  • 系统软件:又称为系统程序,主要用来管理整个计算机系统,监视服务,使系统资源得到合理调度。它包括:操作系统、语言处理程序、数据库管理系统、网络软件、服务程序等。
  • 应用软件:又称为应用程序,它是用户根据需要事先编制的程序。比如 QQ、微信等。

题目传送门

目录:

这是一道我弟拿给我做题目,本来想着普及−应该挺简单的,结果翻车了


题目解读

本题要求我们找出给定区间内的所有“回文质数”1。我一看就想着:先筛质数,再判断是不是回文数~~,结果大意失荆州——开了一个占用4G内存的prime[100000000]~~于是我终于注意到了题目中的提示:

说明/提示

Hint 1: Generate the palindromes and see if they are prime.

提示 1: 找出所有的回文数再判断它们是不是质数(素数).

Hint 2: Generate palindromes by combining digits properly. You might need more than one of the loops like below.

提示 2: 要产生正确的回文数,你可能需要几个像下面这样的循环。

题目翻译来自NOCOW。

USACO Training Section 1.5

产生长度为 5 的回文数:

for (d1 = 1; d1 <= 9; d1+=2) {    // 只有奇数才会是素数
    for (d2 = 0; d2 <= 9; d2++) {
        for (d3 = 0; d3 <= 9; d3++) {
          palindrome = 10000*d1 + 1000*d2 +100*d3 + 10*d2 + d1;//(处理回文数...)
        }
    }
}

于是我便把提示里的代码复制了几份~

AC 代码

#include <bits/stdc++.h>
using namespace std;

// 判断一个数是否为质数
bool isPrime(int n) {
    if (n < 2) return false;
    if (n == 2) return true;
    if (n % 2 == 0) return false;
    
    for (int i = 3; i * i <= n; i += 2) {
        if (n % i == 0) return false;
    }
    return true;
}

vector<int> palindromes;

int main() {
    int st, ed;
    scanf("%d%d", &st, &ed);
    
    // 生成所有可能的回文数
    
    // 1位回文数
    for (int i = 1; i <= 9; i++) {
        palindromes.push_back(i);
    }
    // 偶数位回文数(除了11)都能被11整除,不是回文数
    // 2位回文数
    palindromes.push_back(11);
    
    // 3位回文数
    for (int i = 1; i <= 9; i += 2) { // 只考虑奇数开头,偶数开头的必不是质数
        for (int j = 0; j <= 9; j++) {
            palindromes.push_back(i * 101 + j * 10); // iji形式
        }
    }
    
    // 5位回文数
    for (int i = 1; i <= 9; i += 2) { 
        for (int j = 0; j <= 9; j++) {
            for (int k = 0; k <= 9; k++) {
                palindromes.push_back(i * 10001 + j * 1010 + k * 100); // ijkji形式
            }
        }
    }
    
    // 7位回文数
    for (int i = 1; i <= 9; i += 2) { 
        for (int j = 0; j <= 9; j++) {
            for (int k = 0; k <= 9; k++) {
                for (int l = 0; l <= 9; l++) {
                    palindromes.push_back(i * 1000001 + j * 100010 + k * 10100 + l * 1000); // ijklkji形式
                }
            }
        }
    }
    
    // 9位回文数(虽然超出了1亿,但为了完整性)
    for (int i = 1; i <= 9; i += 2) { 
        for (int j = 0; j <= 9; j++) {
            for (int k = 0; k <= 9; k++) {
                for (int l = 0; l <= 9; l++) {
                    for (int m = 0; m <= 9; m++) {
                        int palindrome = i * 100000001 + j * 10000010 + k * 1000100 + l * 100010 + m * 10000;
                        if (palindrome <= 100000000) { // 只添加不超过1亿的
                            palindromes.push_back(palindrome);
                        }
                    }
                }
            }
        }
    }
    
    // 排序
    sort(palindromes.begin(), palindromes.end());
    
    // 遍历回文数数组,找出其中的质数并输出
    for (int palindrome : palindromes) {
        if (palindrome > ed) break;
        if (palindrome >= st && isPrime(palindrome)) {
            printf("%d\n", palindrome);
        }
    }
    
    return 0;
}

Over!


  1. 即既是回文数又是质数的数