1. 程式人生 > >【折半搜尋 && 左上到右下異或等於 k 的方案數】Codeforces Round #498 (Div. 3) F. Xor-Paths

【折半搜尋 && 左上到右下異或等於 k 的方案數】Codeforces Round #498 (Div. 3) F. Xor-Paths

Step1 Problem:

給你 n*m 的矩陣,從 (1, 1) 到 (n, m) 只能往下和往右走,路徑上的數異或起來等於 k 的路徑數。
資料範圍:
1<=n, m <= 20. 0 <= k <= 1e18. 0 <= a[i][j] <= 1e18.

Step2 Ideas:

n, m 很小,我特別打了個表,從 (1, 1) 到 (11, 11) 的路徑方案數 = 184756。
所以我們可以折半搜尋:
為什麼選擇到 (n+m+2)/2 而不是 (n+m-2)/2,因為 (n+m-2)/2 矩陣太小的話,就沒有滿足條件的點。
第一遍搜尋:從 (1, 1) 到 (n+m+2)/2(包含) 的格子 每個格子構成 sum 的路徑方案數存起來。
第二遍搜尋:從 (n, m) 到 (n+m+2)/2(不包含) 的格子 異或 k 構成 sum ,將之前構成 sum 存起來的方案數的 和為結果。

Step3 Code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n, m;
ll a[25][25];
map<ll, ll> vis[25];
void dfs1(int u, int v, ll sum)//求 (1, 1) 到 (n+m+2)/2 構成 sum 的路徑方案數
{
    if(u+v == (n+m+2)/2) {
        vis[u][sum]++;
        return ;
    }
    if(u <= n)
    dfs1(u+1
, v, sum^a[u+1][v]); if(v <= m) dfs1(u, v+1, sum^a[u][v+1]); } ll ans; void dfs2(int u, int v, ll sum)//(n, m) 到 (n+m+2)/2 { if(u+v == (n+m+2)/2) {//構成 sum 就加起來。 ans += vis[u][sum]; return ; } if(u-1 >= 1) dfs2(u-1, v, sum^a[u][v]); if(v-1 >= 1) dfs2(u, v-1
, sum^a[u][v]); } int main() { ll k; scanf("%d %d %lld", &n, &m, &k); for(int i = 1; i <= n; i++) { for(int j = 1; j <= m; j++) { scanf("%lld", &a[i][j]); } } dfs1(1, 1, a[1][1]); ans = 0; dfs2(n, m, k);//需要異或 k printf("%lld\n", ans); return 0; }