「Codeforces 1006F」Xor-Paths - 双向bfs

建议访问原文出处,获得更佳浏览体验。
原文出处:https://hyp1231.github.io/2018/07/17/20180717-cf1006f/

题意

给定 n×m n × m 的矩阵,以及一个整数 k k
定义路径为,从 (1,1) 出发,只能向右 / 下方走,到达 (n,m) ( n , m ) 的行走方案。
定义路径的权重为,路径经过的整数的 xor x o r 之和。
求权重为 k k 的路径的条数。

链接

Codeforces 1006F

题解

定义从路径一端到 (x,y) 的某条路径上的整数的 xor x o r 之和为 (x,y) ( x , y ) 处的状态

由于 n n m 的范围均为 20 20 之内,考虑暴力搜索。对于每个位置,之后都有两种状态转移(向下 / 向右)。因此我们将矩阵按照 m+n m + n 的值进行分层,如果从起点开始 bfs b f s ,易知每次状态转移使层数加一,每一层的状态数不超过上一层的二倍,故直接 bfs b f s 的状态数不超过 2m+n 2 m + n ,但这个量级是我们不能接受的。

而考虑起点和终点的值都是唯一且确定的,我们可以从起点和终点同时采用 bfs b f s 。因此我们规定 m+n2 m + n 2 为中心层,两个 bfs b f s 一到中心层则停止搜索,这样在中心层,如果有重合的状态则说明有合法路径。

为了记录合法路径的数目,我们需要记录某一个 bfs b f s (这里不妨设为从起点出发的)到达状态 val v a l 的路径数。在实现上,我们使用 STL S T L 库中的 map m a p ,形成状态到路径数的映射。这样先实现从起点出发的 bfs b f s ,得到中间层每个位置各个状态的路径数;再做另一个 bfs b f s ,对于每个到中间层的状态,把第一个 bfs b f s 中计算出的该状态对应的路径数加到总的 ans a n s 中。

这样搜索到中间层的状态数不超过 2m+n2 2 m + n 2 map m a p 的复杂度 O(log2m+n2)=O(m+n2log2)=O(m+n2) O ( log ⁡ 2 m + n 2 ) = O ( m + n 2 log ⁡ 2 ) = O ( m + n 2 ) ,总复杂度 O(m+n22m+n2) O ( m + n 2 ⋅ 2 m + n 2 )

代码

#include <iostream>
#include <queue>
#include <map>

typedef long long LL;

const int N = 20;

struct State {
    int x, y;
    LL val;
    State(int x = 0, int y = 0, LL val = 0) : x(x), y(y), val(val) {}
};

int n, m, mid;
LL a[N][N], k;

// 记录中间层 状态->路径数 的映射
// 由于 x + y = (n + m) / 2,我们确定了 x 即可确定 y
// 因此 res[x][st] 代表中间层横坐标为 x 的点,状态为 st 的路径数目
std::map<LL, int> res[N << 1];

int main() {
    std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);

    std::cin >> n >> m >> k;
    mid = (n + m - 2) >> 1;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            std::cin >> a[i][j];
        }
    }

    LL ans = 0;
    std::queue<State> que1, que2;
    que1.push(State(0, 0, a[0][0]));
    while (!que1.empty()) {
        State tmp = que1.front(); que1.pop();
        int x = tmp.x, y = tmp.y;
        LL val = tmp.val;
        if (x + y == mid) { // 到达中心线
            if (res[x].count(val)) {
                ++res[x][val];
            } else {
                res[x][val] = 1;
            }
        } else {
            if (x + 1 != n) que1.push(State(x + 1, y, val ^ a[x + 1][y]));
            if (y + 1 != m) que1.push(State(x, y + 1, val ^ a[x][y + 1]));
        }
    }

    que2.push(State(n - 1, m - 1, k));
    while (!que2.empty()) {
        State tmp = que2.front(); que2.pop();
        int x = tmp.x, y = tmp.y;
        LL val = tmp.val;
        if (x + y == mid) {
            ans += res[x][val];
        } else {
            if (x - 1 >= 0) que2.push(State(x - 1, y, val ^ a[x][y]));
            if (y - 1 >= 0) que2.push(State(x, y - 1, val ^ a[x][y]));
        }
    }

    std::cout << ans << std::endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值