CodeForces 1006F Xor-Paths|Meet in the middle

题目链接

题目大意:给一个 \(n\times m\) 的网格图。每个点有一权值 \(a_{i,j}\),现在要从 \((1,1)\) 走到 \((n,m)\),且仅能向右或向下,求路径经过的点的权值异或和恰好为\(k\)的方案数。
\(1 \le n,m \le 20; 0 \le k \le 10^{18}\)

题目思路:
看上去找不到什么算法去解决本题,那么遇事不决上暴搜,复杂度约为 \(2^{n+m}\),显然不能通过,并且这是CF题没部分分
但是我们还是找不到什么解法,只好考虑优化搜索(这一步在考场上尤为重要)。
我们发现,在本题中,我们既可以从起点开始搜索,也可以从终点开始搜索。显然这是等价的。这条性质等下将用到。
现在引入一个概念:双向搜索/折半搜索/Meet in the middle。顾名思义,其方法是分别从起点和终点开始,做一半路程的搜索,并保存下结果,最后进行合并。这个方法的原理就是上文从起点搜和从终点搜等价的性质。
于是,搜索的时间复杂度由 \(2^{n+m}\) 变为 \(2\times 2^{\frac{n+m}{2}}\),可以接受。
当然,合并答案也是很重要的一步。考虑如何快速得出答案:我们使用\(map\)来保存每个中间点的答案,从起点搜时记录,从终点搜时取出即可。由于结果数尚不算多,因此也可以接受
上代码

#include<bits/stdc++.h>
using namespace std;
map<long long,int> f[30][30];
long long n,m,k,z[30][30],a[30][30],ans,t;
void q_dfs(int x,int y,long long kk)
{	
	if (x>n||y>m) return ;
	if (x+y==((n+m+2)>>1)) 
	{f[x][y][kk]++;return ;}
	q_dfs(x+1,y,kk^a[x+1][y]);
	q_dfs(x,y+1,kk^a[x][y+1]);
}//从起点开始搜
void h_dfs(int x,int y,long long kk)
{
	if (x<1||y<1) return ;
	if (x+y==((n+m+2)>>1))
	{
		if (f[x][y].find(k^(kk^a[x][y]))==f[x][y].end()) return ;
		ans+=f[x][y][k^(kk^a[x][y])];
		return ;
	}
	h_dfs(x-1,y,kk^a[x-1][y]);
	h_dfs(x,y-1,kk^a[x][y-1]);
}//从终点开始搜
int main()
{
	scanf("%lld%lld%lld",&n,&m,&k);
	for (int i=1;i<=n;i++)
	  for (int j=1;j<=m;j++)
	  {
	    scanf("%lld",&a[i][j]);
	    f[i][j].clear();
	  }
	q_dfs(1,1,a[1][1]);
	h_dfs(n,m,a[n][m]);
	cout<<ans<<endl;
	return 0;
}
posted @ 2020-12-04 07:55  fmj_123  阅读(91)  评论(0编辑  收藏  举报