专栏文章

题解:P11372 「CZOI-R2」加训

P11372题解参与者 3已保存评论 2

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
2 条
当前快照
1 份
快照标识符
@miqvwlkk
此快照首次捕获于
2025/12/04 11:36
3 个月前
此快照最后确认于
2025/12/04 11:36
3 个月前
查看原文

题目大意

机房被看作维正方体,有 mm 个摸鱼的、xx 个障碍与 yy 个教练处于不同坐标。教练发现摸鱼,需满足坐标有且仅有 k1k-1 维相同且连线无其他干扰。

解题思路

  1. 方向与人数上限:因 kk 维空间,教练最多有 2k2k 个观察方向,理论最多发现 2k2k 个摸鱼。
  2. 预处理最近障碍:将教练当作障碍,遍历所有障碍与教练比较。对满足 k1k-1 维坐标差异的障碍,依其与教练坐标大小关系,更新对应方向最近障碍信息。
  3. 处理学生:对每个学生,先查是否满足 k1k-1 维坐标差异条件,不符则跳过。符合时,对比学生与对应方向最近障碍到教练距离,更近则更新学生为新最近对象,标记并统计。
  4. 统计结果:统计各方向最近为学生的方向数,即教练发现摸鱼数量。
注释非常详细!
CPP
#include<bits/stdc++.h>
using namespace std;
const int MAX_SIZE = 1e3 + 5;
// 机房空间(看作k维正方体)的边长
int n;  
// 机房空间的维数
int dt;  
// OIer(正在摸鱼的人员)的数量
int ot;  
// 原本障碍的数量
int oc;  
// 教练的数量
int ct;  

// os数组用于存储OIer的坐标信息,第一维表示第几个OIer,第二维表示坐标维度
int os[MAX_SIZE][5];  
// all数组用于存储障碍(包含后面当作障碍处理的教练)的坐标信息,第一维表示第几个障碍(教练也算在内了),第二维表示坐标维度
int all[2 * MAX_SIZE][5];  
// cs数组用于单独存储教练的坐标信息,方便后续处理时区分使用,第一维表示第几个教练,第二维表示坐标维度
int cs[MAX_SIZE][5];  

// nr数组用于存储每个方向上到教练最近的障碍相关信息,nr[0][i]表示在某个维度上坐标小于教练坐标方向(可理解为“左”方向)的最近障碍坐标
// nr[1][i]表示在某个维度上坐标大于教练坐标方向(可理解为“右”方向)的最近障碍坐标
int nr[2][5];  
// vis数组用于标记每个方向上到教练最近的是不是学生,vis[0][i]对应“左”方向,vis[1][i]对应“右”方向
bool vis[2][5];  

// solve函数用于处理单个教练能发现的摸鱼OIer的数量,参数now表示当前处理的是第几个教练
void solve(int now) {
    int fn = 0;
    // 初始化nr数组,将nr[0]各元素置为0,通常用于记录较小值(类似左方向)时的初始比较基础
    memset(nr[0], 0, sizeof(nr[0]));  
    // 初始化nr[1]各元素为一个较大值(0x7f在本题情境下可当作一个相对很大的值用于后续比较更新)
    // 用于记录较大值(类似右方向)时的初始比较基础,方便后续找到更小的更近的值来更新
    memset(nr[1], 0x7f, sizeof(nr[1]));  
    // 初始化vis数组,将所有标记都置为false,表示初始时各方向上最近的都不是学生
    memset(vis, 0, sizeof(vis));  

    // 预处理障碍(这里的障碍包含了当作障碍处理的教练),遍历所有的障碍
    for (int i = 1; i <= oc; i++) {
        int solve = 0;  // solve用于记录当前障碍与教练有几个维度的坐标是相同的,初始化为0
        int dif = -1;  // dif用于记录第一个不同的维度编号,初始化为 -1,表示未找到不同维度
        // 遍历每个维度,判断当前障碍与当前教练在该维度上的坐标是否相同
        for (int j = 1; j <= dt; j++) {
            if (all[i][j]!= cs[now][j]) {
                dif = j;  // 如果坐标不同,记录下这个不同的维度编号
            } else {
                solve++;  // 如果坐标相同,相同维度数量加1
            }
        }
        // 如果相同维度数量不等于dt - 1,说明不满足发现摸鱼条件里关于坐标维度差异的要求,直接跳过该障碍
        if (solve!= dt - 1) continue;  
        // 如果在某个维度上,当前障碍的坐标小于教练在该维度的坐标,更新对应“左”方向上的最近障碍坐标
        if (all[i][dif] < cs[now][dif])
            nr[0][dif] = max(nr[0][dif], 
                                                             all[i][dif]);  
        else
            // 如果在某个维度上,当前障碍的坐标大于教练在该维度的坐标,更新对应“右”方向上的最近障碍坐标
            nr[1][dif] = min(nr[1][dif], 
                                                              all[i][dif]);  
    }

    // 对每个学生进行处理,遍历所有的学生
    for (int i = 1; i <= ot; i++) {
        int solve = 0;
        int dif = -1;
        // 同样遍历每个维度,判断当前学生与当前教练在各维度上的坐标异同情况
        for (int j = 1; j <= dt; j++) {
            if (os[i][j]!= cs[now][j]) {
                dif = j;
            } else {
                solve++;
            }
        }
        // 如果不满足有且仅有dt - 1维坐标不同的条件,跳过该学生
        if (solve!= dt - 1) continue;  
        // 如果学生在某个维度上的坐标小于教练坐标,且大于该维度“左”方向上已记录的最近障碍坐标
        if (os[i][dif] < cs[now][dif] && os[i][dif] > nr[0][dif]) {
            // 更新“左”方向上的最近坐标为该学生的坐标
            nr[0][dif] = os[i][dif];  
            // 如果该方向上之前最近的不是学生,说明发现了新的可被发现摸鱼的学生,数量加1,并标记该方向最近的是学生
            if (!vis[0][dif]) {  
                fn++;
                vis[0][dif] = 1;
            }
        }
        // 如果学生在某个维度上的坐标大于教练坐标,且小于该维度“右”方向上已记录的最近障碍坐标
        if (os[i][dif] > cs[now][dif] && 
            os[i][dif] < nr[1][dif]) {
            // 更新“右”方向上的最近坐标为该学生的坐标
            nr[1][dif] = os[i][dif];  
            // 如果该方向上之前最近的不是学生,说明发现了新的可被发现摸鱼的学生,数量加1,并标记该方向最近的是学生
            if (!vis[1][dif]) {  
                fn++;
                vis[1][dif] = 1;
            }
        }
    }
    // 输出当前教练能发现的摸鱼OIer的数量
    cout << fn <<' ';  
}

int main() {
    // 输入机房空间的边长、维数、OIer的数量、原本障碍的数量以及教练的数量
    cin >> n >> dt >> ot >> oc >> ct;  
    // 循环读取每个OIer的坐标信息,外层循环控制第几个OIer,内层循环控制坐标维度
    for (int i = 1; i <= ot; i++)
        for (int j = 1; j <= dt; j++)
            cin >> os[i][j];  
    // 循环读取每个障碍的坐标信息,外层循环控制第几个障碍,内层循环控制坐标维度
    for (int i = 1; i <= oc; i++)
        for (int j = 1; j <= dt; j++)
            cin >> all[i][j];  
    // 循环读取每个教练的坐标信息,外层循环控制第几个教练,内层循环控制坐标维度
    // 同时将教练的坐标信息添加到障碍坐标数组all中,将教练当作障碍处理,方便后续统一处理
    for (int i = 1; i <= ct; i++)
        for (int j = 1; j <= dt; j++) {
            cin >> cs[i][j];
            all[oc + i][j] = cs[i][j];
        }
    // 更新障碍的总数,将教练数量加到原本的障碍数量上,因为教练已经当作障碍看待了
    oc += ct;  
    // 对每个教练调用solve函数,统计并输出每个教练能发现的摸鱼OIer的数量
    for (int i = 1; i <= ct; i++)
        solve(i);  
    return 0;
}

评论

2 条评论,欢迎与作者交流。

正在加载评论...