Codeforces487D - Conveyor Belts - 分块、记忆化搜索

D. Conveyor Belts

题意: 给出一个二维 n × m n\times m n×m 的传送带地图,其中>字符表示当前位置 ( x , y ) (x,y) (x,y) 传送至 ( x , y + 1 ) (x,y+1) (x,y+1)<表示传送至 ( x , y − 1 ) (x,y-1) (x,y1)^表示传送至 ( x − 1 , y ) (x-1,y) (x1,y)。现在有 q q q 次询问,询问有两种类型,其中A x y表示询问在点 ( x i , y i ) (x_i,y_i) (xi,yi) 处放置物品,最终会被传送到哪?(物品如果陷入循环则输出-1,否则物品到达边界则结束),而C x y c表示修改地图第 ( x , y ) (x,y) (x,y) 坐标位置的传送带为c类型。

  • 1 ≤ n , q ≤ 1 0 5 1\le n,q\le 10^5 1n,q105
  • 1 ≤ m ≤ 10 1\le m\le 10 1m10

题解: 考虑对 n n n 分块,暴力维护块内每个点的最终位置(要么边界,要么陷入块内陷入循环,要么跳到其它块),这样查询和修改都是 O ( n m ) \mathcal{O}(\sqrt {nm}) O(nm )。维护最终位置的方法是用dp[x][y]表示点 ( x , y ) (x,y) (x,y) 到达的最终位置,递归地记忆化搜索即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int maxn = 1e5 + 5;
char s[maxn][12];
int pos[maxn], L[maxn], R[maxn], n, m, q;
pii dp[maxn][12];
pii dfs(int x, int y, int lim) {
    if (dp[x][y].first != 0 || dp[x][y].second != 0) return dp[x][y];
    pii res = {0, 0};
    if (s[x][y] == '<') {
        if (y == 1) res = {x, y-1};
        else if (s[x][y-1] == '>') res = {-1, -1};
        else res = dfs(x, y-1, lim);
    } else if (s[x][y] == '>') {
        if (y == m) return res = {x, y+1};
        else if (s[x][y+1] == '<') res = {-1, -1};
        else res = dfs(x, y+1, lim);
    } else if (s[x][y] == '^') {
        if (x == lim) res = {x-1, y};
        else res = dfs(x-1, y, lim);
    }
    return dp[x][y] = res;
}
int main() {
    scanf("%d%d%d", &n, &m, &q);
    int block = sqrt(n + 0.5);
    for (int i = 1; i <= n; i++) {
        scanf("%s", s[i]+1);
        pos[i] = (i-1) / block + 1;
    }
    int tot = n / block;
    if (n % block) tot++;
    for (int i = 1; i <= tot; i++) {
        L[i] = (i-1) * block + 1;
        R[i] = i * block;
        if (i == tot) R[i] = min(R[i], n);
        for (int j = L[i]; j <= R[i]; j++) {
            for (int k = 1; k <= m; k++) dp[j][k] = dfs(j, k, L[i]);
        }
    }
    while (q--) {
        char op[2], c[2];
        int x, y;
        scanf("%s%d%d", op, &x, &y);
        if (op[0] == 'C') {
            scanf("%s", c);
            s[x][y] = c[0];
            for (int i = L[pos[x]]; i <= R[pos[x]]; i++) {
                for (int j = 1; j <= m; j++) dp[i][j] = {0, 0};
            }
            for (int i = L[pos[x]]; i <= R[pos[x]]; i++) {
                for (int j = 1; j <= m; j++) dp[i][j] = dfs(i, j, L[pos[x]]);
            }
        } else if (op[0] == 'A') {
            while (1) {
                if (x == -1 && y == -1) break;
                if (x == 0 || y == 0 || x == n+1 || y == m+1) break;
                int tx = dp[x][y].first;
                int ty = dp[x][y].second;
                x = tx, y = ty;
            }
            printf("%d %d\n", x, y);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值