首页 > 其他分享 >2024牛客暑期多校训练营1 I.Mirror Maze(题解)

2024牛客暑期多校训练营1 I.Mirror Maze(题解)

时间:2024-07-17 10:18:23浏览次数:8  
标签:状态 tem int 题解 多校 dfs else 2024 dir

题意

给一个 \(n \times m\) 的二维char数组,由4种镜子组成,'\', '/', '-', '|',镜面反射规则就是根据光线的方向和镜面的角度符合直觉的反射,然后有多组询问 \(q \leq 10^6\),每次给定起始位置和光线方向,求该光会经过多少面不同的镜子的反射。

思路

首先根据数据范围,发现肯定需要预处理答案,然后每次 \(O(1)\) 回答,所以考虑如何进行预处理,首先显然预处理是开一个 \(ans[n][m][4]\) 的数组,把每个位置的4种方向对应答案都存起来。(注意:这里的 \(ans[i][j][k]\) 表示的是方向为 \(k\) 的光线正要射向 \(ch[i][j]\) 的镜子时,总的会反射过的镜子数。而题目询问时起点的镜子不算,所以询问时要先根据方向移动一格,才对应上前面设置的状态的含义)

然后需要一些性质,一束光在这个矩阵里要么是陷入死循环,要么就一定会射出去越界消失。所以需要分别处理这两种情况。

还有最重要的一个性质就是,因为光路可逆, 所以每个状态(即上面的总共 4nm种状态),一定只对应一条光路,也就是光路之间一定是没有重合的。这就启示了,可以直接爆搜,爆搜出来的状态不会有重复的。

然后就到了如何把环与链单独抽出来的问题,这个也是本题的难点(题解里只是讲了思路,具体如何抽,感觉也是非常考察代码能力)。赛后看了别人的代码,发现了巧妙的抽法。

还是由于前面的性质,对于一个链,最终肯定是射向出界,所以我们可以反过来,从四个边界往里射入光线,这些状态一定是可以作为链的起点的,然后根据题意爆搜,直到再次出界为止。把过程中经历的所有状态全部存起来(可以用vector<tuple<int, int, int>>储存),这些状态就是链上的状态,然后倒叙遍历状态,维护每个状态的前缀会反射过的镜子(实现上,我先用一个函数表示一个状态是否会反射到那个位置的镜子,再用一个set把反射到的镜子的位置存起来),然后就处理好了链。

接下来考虑处理环,因为前面已经把所有可能的链都处理好,所以直接暴力 for 循环遍历总的 4nm种状态,如果有状态未访问过,说明这个状态一定在环上,直接从它开始再暴力dfs,直到又到达一个已经访问过的状态,说明环走完了,对这些状态全部反射过的镜子用set存一下,然后set的大小就是这个环上所有状态的答案。

最后的询问,先根据方向偏移一下,然后直接输出预处理的答案即可。代码细节如下:

点击查看代码
#include <bits/stdc++.h>
#define pii pair<int, int>
#define tii tuple<int, int, int>
#define all(a) a.begin(), a.end()
using namespace std;
const int maxn = 1e3 + 10;
char ch[maxn][maxn];
int n, m, ans[maxn][maxn][4];
int vis[maxn][maxn][4];
vector<tii> vec;
bool istan(int x, int y, int dir) {  // 标记某个状态是否有经过镜子的反射
    char tem = ch[x][y];
    if (tem == '/' || tem == '\\')
        return 1;
    if (tem == '-' && (dir == 0 || dir == 1))
        return 1;
    if (tem == '|' && (dir == 2 || dir == 3))
        return 1;
    return 0;
}
void dfs(int x, int y, int dir) {
    // 根据题意模拟反射过程,越界或者访问过退出
    if (x < 1 || x > n || y < 1 || y > m)  // 链的退出条件
        return;
    if (vis[x][y][dir])  // 环的退出条件
        return;
    vec.push_back(tii(x, y, dir));
    vis[x][y][dir] = 1;
    if (ch[x][y] == '/') {
        if (dir == 0) {
            dfs(x, y + 1, 3);
        } else if (dir == 1) {
            dfs(x, y - 1, 2);
        } else if (dir == 2) {
            dfs(x + 1, y, 1);
        } else {
            dfs(x - 1, y, 0);
        }
    } else if (ch[x][y] == '\\') {
        if (dir == 0) {
            dfs(x, y - 1, 2);
        } else if (dir == 1) {
            dfs(x, y + 1, 3);
        } else if (dir == 2) {
            dfs(x - 1, y, 0);
        } else {
            dfs(x + 1, y, 1);
        }
    } else if (ch[x][y] == '-') {
        if (dir == 0) {
            dfs(x + 1, y, 1);
        } else if (dir == 1) {
            dfs(x - 1, y, 0);
        } else if (dir == 2) {
            dfs(x, y - 1, 2);
        } else
            dfs(x, y + 1, 3);
    } else if (ch[x][y] == '|') {
        if (dir == 0)
            dfs(x - 1, y, 0);
        else if (dir == 1)
            dfs(x + 1, y, 1);
        else if (dir == 2) {
            dfs(x, y + 1, 3);
        } else {
            dfs(x, y - 1, 2);
        }
    }
}
void solve1(int x, int y, int dir) {
    // 处理链
    vec.clear();
    dfs(x, y, dir);
    reverse(all(vec));  // 倒叙遍历状态更新答案
    set<pii> s;
    for (auto [a, b, c] : vec) {
        if (istan(a, b, c))
            s.insert(pii(a, b));
        ans[a][b][c] = s.size();
    }
}
void solve2(int x, int y, int dir) {
    // 处理环
    vec.clear();
    dfs(x, y, dir);
    set<pii> s;
    for (auto [a, b, c] : vec) {
        if (istan(a, b, c))
            s.insert(pii(a, b));
    }
    for (auto [a, b, c] : vec)
        ans[a][b][c] = s.size();  // 环上的答案都一样
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> ch[i][j];
    // 从边界射入的光线一定是链
    for (int i = 1; i <= n; i++)
        solve1(i, 1, 3), solve1(i, m, 2);
    for (int j = 1; j <= m; j++)
        solve1(1, j, 1), solve1(n, j, 0);
    // 还未访问的状态,一定是环上的状态
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            for (int k = 0; k < 4; k++)
                if (!vis[i][j][k]) {
                    solve2(i, j, k);
                }
    int q;
    cin >> q;
    map<string, int> mp;
    mp["above"] = 0;
    mp["below"] = 1;
    mp["left"] = 2;
    mp["right"] = 3;
    while (q--) {
        int x, y;
        string str;
        cin >> x >> y >> str;
        // 先根据方向偏移一格
        int tem = mp[str];
        if (tem == 0)
            x--;
        else if (tem == 1)
            x++;
        else if (tem == 2)
            y--;
        else
            y++;
        cout << ans[x][y][tem] << "\n";
    }
    return 0;
}

标签:状态,tem,int,题解,多校,dfs,else,2024,dir
From: https://www.cnblogs.com/TJUHuangTao/p/18306749

相关文章

  • 【App渗透】BurpSuite插件-Brida 2024最新自动加解密Custom plugins演示
    文章目录前言一、测试app的客户端和服务端二、BurpSuite设置代理三、反编译apk文件四、编写brida/fridahook脚本五、Customplugins自动加解密六、本期送书《二进制安全基础》如何领书总结前言之前有写过如何安装brida的文章和视频讲解,大家感兴趣的可以看看之前......
  • ARC_069 D - Menagerie 题解
    atcoder一道很有意思的模拟题啊。思路很重要。首先,我们只要知道连续两只动物的身份,就可以根据\(s\)推出所有动物的身份。不妨假设我们知道第一只和第二只动物的身份,一共有几种情况呢?用\(1\)代表羊,\(0\)代表狼。那么,共有\(2^2=4\)种情况,分别为:00011011然后我......
  • 2024年华为OD机试真题-符号运算-(C++/Java/python)-OD统一考试(C卷D卷)
      2024华为OD机试真题目录-(B卷C卷D卷)-【C++JavaPython】    题目描述给定一个表达式,求其分数计算结果。表达式的限制如下:所有的输入数字皆为正整数(包括0)仅支持四则运算(+-*,/)和括号结果为整数或分数,分数必须化为最简格式(比如6,3/4,7/8,90/7)除数可能为0,如果遇到......
  • 题解|2024暑期牛客多校01
    【原文链接】太菜了就先挂4题,其他的写出来了再回来补QwQA.ABitCommon组合数学题目大意题目概括:给定两个整数nnn和m......
  • 2024年春季新番简评
    2024年春季新番简评四月:春天,春天与恋爱、美好等常常联系在一起。四月新番通常也是一年之中优秀动画最多的一个季度。2024年的春天,是一个原创动画和续作动画充满话题度的春天。忙完了期末考和期末实验,终于有机会来写新番记录了,这个学期真的有点心力憔悴,选了三门专选课,但是感觉又......
  • [题解]POJ2074 Line of Sight
    POJ2074LineofSight题意简述多测。给定若干条线段,全部与\(x\)轴平行。其中有\(2\)条线段表示房子和人行道(虽然翻译不是人行道就是了),保证房子在人行道上面。其他线段表示障碍物(不保证在房子和人行道之间)。请找出人行道上最长的连续部分,使得在这中间可以完整地看到房子的全......
  • Intel Management Engine WMI Provider 2408.5.4.0 20240221 驱动程序 Intel管理引擎
    驱动程序"IntelManagementEngineWMIProvider2408.5.4.0"是指Intel管理引擎的一部分,它通过Windows管理仪表(WMI)提供对管理引擎功能的访问和管理。这些驱动程序通常用于管理和配置Intel管理引擎的功能,包括安全功能、远程访问以及系统监控等。如果您需要安装或更新这个驱......
  • 2024.7.16
    ###2024.7.16【Ineverlose.Ieitherwinorlearn.】###Tuesday六月十一---##0/1Trie学习笔记使用trie维护异或极值可以使用0/1Trie,这是一种以{0,1}为字符集的Trie树,他支持修改和全局加一使用异或操作时,我们其实并不需要知道每一位上的具体值,只需要知道每一位......
  • YC317A [ 20240708 CQYC省选模拟赛 T1 ] 划分(partition)
    题意给定一个长度为\(n+m\)的二进制数,你需要将这个二进制数划分别划分为长度为\(n\)的二进制数\(a\)与长度为\(m\)的二进制数\(b\)。你需要输出\(a+b\)的二进制形式。\(n\le10^6\)。Sol考虑发现一些性质。设\(n\gem\),则:考虑最优方案给\(a\)与\(b......
  • (20240716)无机非金属材料工学(1)原料及粉体制备
    一、概述1.水泥的生产方法:水泥的生产历来就有干法生产和湿法生产两种最典型的方法,随着干法生产水泥技术和装备的高度、快速发展,同时出于节能的考虑,水泥的湿法生产在全世界基本上已经停用。2.粉碎无机非金属材料的原料大多来自天然的硬质矿物,多数为块状必须对其实施破碎......