蓝桥杯备战day3

本文主要记录笔者蓝桥杯备战过程

目录

前言

一、基础题:乘法表

二、进阶题:奇偶覆盖

总结


前言

本文记录笔者备战蓝桥杯第三天的过程,包括一道基础题和一道进阶题。


一、基础题:乘法表

我先把题目测试链接粘贴在下面:

P8723 [蓝桥杯 2020 省 AB3] 乘法表 - 洛谷

题目详细叙述如下:

我的思路是通过signal数组建立0-9、A-Z与0-35的直接映射,以双重上三角循环(i从1到p-1、j从1到i)生成乘数组合,先计算十进制乘积sum,再根据sum是否小于p判断其p进制为1位或2位,分别通过signal数组索引取值存入tmp数组,最终按p进制乘数*p进制乘数=p进制乘积的格式输出,且仅在非行尾式子后加空格,形成无多余空格、布局整齐的p进制乘法表。我的代码如下:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

int main(){
    int p = 0;
    scanf("%d", &p);
    char* signal = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    for(int i = 1; i < p; i++){
        for(int j = 1; j <= i; j++){
            char tmp[3] = {0};
            int sum = i * j;
            if(sum < p){
                tmp[0] = signal[sum];
            }else{
                tmp[1] = signal[sum % p];
                tmp[0] = signal[sum / p];
            }
            printf("%c*%c=%s", signal[i], signal[j], tmp);
            if (j != i) {
                printf(" ");
            }
        }
        printf("\n");
    }
    return 0;
}

二、进阶题:奇偶覆盖

我先把题目测试链接粘贴在下面:

P8734 [蓝桥杯 2020 国 A] 奇偶覆盖 - 洛谷

题目详细叙述如下:

这道题则是涉及到了扫描线和线段树,我问了下AI,它是这么做的,将每个矩形拆分为上下两条水平边界扫描线(上边界标记添加覆盖,下边界标记删除覆盖),按y坐标升序排序扫描线;对所有矩形的x坐标离散化,构建线段树维护x轴区间的覆盖次数,实时计算当前y区间内奇数覆盖次数的长度和≥2次偶数覆盖次数的长度;扫描线从下到上移动,每次用当前有效长度乘以扫描线移动的高度差,分别累加得到两类覆盖面积,最终输出结果,时间复杂度O (nlogn),适用于大数据量场景。代码如下:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ll long long
#define N 1000005
#define lson p<<1
#define rson p<<1|1

// 扫描线结构体(存储x区间、y坐标、标记:添加1/删除-1)
typedef struct Line {
    ll l, r, h;
    int mark;
} Line;

// 线段树结构体(维护x轴区间的覆盖状态)
typedef struct segment_tree {
    ll l, r;          // 对应X数组的左右索引(实际坐标通过X数组映射)
    ll len1, len2;    // len1:奇数覆盖次数的长度;len2:≥2次偶数覆盖次数的长度
    int sum;          // 区间覆盖次数
} segment_tree;

// 全局变量(避免函数传参冗余,适配线段树全局存储)
segment_tree t[N << 2];  // 线段树数组(4倍空间)
Line line[N << 1];       // 扫描线数组(2n个边界)
ll X[N << 1];            // 存储所有x坐标(用于离散化)
ll ans1, ans2;           // 结果:ans1=奇数覆盖面积,ans2=≥2次偶数覆盖面积
ll n;                   // 矩形个数(最终会变成2n,存储扫描线数量)

// 快速读入函数(C语言实现,兼容大数字)
void read(ll *a) {
    char ch;
    int f = 1;
    ll k = 0;
    ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        k = k * 10 + ch - '0';
        ch = getchar();
    }
    *a = k * f;
}

// qsort比较函数:扫描线按y坐标升序排序
int cmp_line(const void *x, const void *y) {
    Line *a = (Line *)x;
    Line *b = (Line *)y;
    return a->h < b->h ? -1 : 1;
}

// qsort比较函数:x坐标升序排序(用于离散化)
int cmp_x(const void *x, const void *y) {
    ll *a = (ll *)x;
    ll *b = (ll *)y;
    return *a < *b ? -1 : 1;
}

// 去重函数:对排序后的X数组去重,返回去重后长度
int unique(ll *arr, int len) {
    int tot = 1;
    for (int i = 2; i <= len; i++) {
        if (arr[i] != arr[tot]) {
            arr[++tot] = arr[i];
        }
    }
    return tot;
}

// 线段树push_up:更新当前节点的len1和len2(核心逻辑)
void push_up(int p) {
    if (t[p].sum == 0) {  // 无覆盖,继承左右子节点的长度
        t[p].len1 = t[lson].len1 + t[rson].len1;
        t[p].len2 = t[lson].len2 + t[rson].len2;
    } else if (t[p].sum % 2 == 0) {  // 偶数次覆盖(≥2)
        t[p].len1 = t[lson].len1 + t[rson].len1;  // 奇数长度继承子节点
        t[p].len2 = X[t[p].r] - X[t[p].l] - t[p].len1;  // 总长度 - 奇数长度 = 偶数≥2长度
    } else {  // 奇数次覆盖
        t[p].len2 = t[lson].len1 + t[rson].len1;  // 子节点奇数长度变为当前偶数≥2长度
        t[p].len1 = X[t[p].r] - X[t[p].l] - t[p].len2;  // 总长度 - 偶数≥2长度 = 奇数长度
    }
}

// 线段树构建:初始化区间[l, r](基于X数组的索引)
void build(int p, int l, int r) {
    t[p].l = l;
    t[p].r = r;
    t[p].sum = 0;
    t[p].len1 = 0;
    t[p].len2 = 0;
    if (l + 1 == r) {  // 叶子节点(对应X[l]到X[r]的区间)
        return;
    }
    int mid = (l + r) >> 1;
    build(lson, l, mid);
    build(rson, mid, r);
}

// 线段树修改:对x轴区间[L, R]添加覆盖标记w(1=添加,-1=删除)
void modify(int p, ll L, ll R, int w) {
    // 区间无交集,直接返回
    if (X[t[p].r] <= L || X[t[p].l] >= R) {
        return;
    }
    // 完全覆盖当前节点的x区间,更新sum并push_up
    if (L <= X[t[p].l] && X[t[p].r] <= R) {
        t[p].sum += w;
        push_up(p);
        return;
    }
    // 递归修改左右子节点
    modify(lson, L, R, w);
    modify(rson, L, R, w);
    push_up(p);  // 回溯更新当前节点
}

int main() {
    read(&n);
    ll xa, xb, ya, yb;
    for (ll i = 1; i <= n; i++) {
        read(&xa);
        read(&ya);
        read(&xb);
        read(&yb);
        // 收集所有x坐标(用于离散化)
        X[2 * i - 1] = xa;
        X[2 * i] = xb;
        // 拆分矩形为上下边界扫描线:上边界(y=ya)添加,下边界(y=yb)删除
        line[2 * i - 1] = (Line){xa, xb, ya, 1};
        line[2 * i] = (Line){xa, xb, yb, -1};
    }
    n <<= 1;  // 扫描线总数=2*原矩形数
    // 排序:扫描线按y升序,x坐标按升序(用于离散化)
    qsort(line + 1, n, sizeof(Line), cmp_line);
    qsort(X + 1, n, sizeof(ll), cmp_x);
    // x坐标离散化(去重),得到有效x区间数量
    int tot = unique(X, n);
    // 构建线段树(基于离散化后的x索引)
    build(1, 1, tot);
    // 扫描线遍历:从下到上处理每个y区间
    for (ll i = 1; i < n; i++) {
        // 修改当前扫描线对应的x区间覆盖状态
        modify(1, line[i].l, line[i].r, line[i].mark);
        // 累加面积:当前有效长度 × 扫描线移动的高度差(line[i+1].h - line[i].h)
        ans1 += t[1].len1 * (line[i + 1].h - line[i].h);
        ans2 += t[1].len2 * (line[i + 1].h - line[i].h);
    }
    // 输出结果
    printf("%lld\n%lld\n", ans1, ans2);
    return 0;
}

总结

本文记录了蓝桥杯备战的第三天学习内容,包括一道基础题和一道进阶题的解题过程。基础题"乘法表"通过建立字符映射和双重循环实现了p进制乘法表的生成。进阶题"奇偶覆盖"则运用了扫描线算法和线段树技术,通过离散化处理和区间维护计算了奇数覆盖和偶数覆盖的面积。两题分别展示了基础编程能力和高级算法应用,体现了从简单到复杂的编程思维训练过程。

相关推荐
佩奇大王10 小时前
蓝桥杯 P8772
职场和发展·蓝桥杯
云泽80810 小时前
蓝桥杯枚举算法精讲:从普通枚举到二进制枚举
算法·职场和发展·蓝桥杯
是苏浙13 小时前
备战蓝桥杯day4
蓝桥杯
魂梦翩跹如雨1 天前
P10424 [蓝桥杯 2024 省 B] 好数——Java解答
java·蓝桥杯
小年糕是糕手1 天前
【C++】类和对象(四) -- 取地址运算符重载、构造函数plus
c语言·开发语言·数据结构·c++·算法·leetcode·蓝桥杯
_OP_CHEN2 天前
【算法基础篇】(二十四)数据结构之并查集拓展:从 “单一关系” 到 “复杂约束”,这篇带你解锁进阶玩法!
数据结构·蓝桥杯·并查集·算法竞赛·acm/icpc·带权并查集·扩展域并查集
魂梦翩跹如雨2 天前
P8615 [蓝桥杯 2014 国 C] 拼接平方数——Java解答
java·c语言·蓝桥杯
迈巴赫车主2 天前
蓝桥杯20534爆破 java
java·数据结构·算法·职场和发展·蓝桥杯
是苏浙2 天前
蓝桥杯备战day2
蓝桥杯