【题目背景】
众所周知,xcj是个万人迷,拥有迷妹无数。眼红的jdr(太可爱啦!!!)和wly决定建立“仇帅者联盟”,打倒xcj以解除长期以来xcj对妹子的垄断。于是他们制定了一个作战计划...
【题目描述】
将实验的校园看成是一个n*m的地图,左上角为 (1,1) ,jdr和wly从起点 (1,1) 出发(只能向下或向右走)分别走各自的最短路到达xcj的活动地点 (n,m) 进行刺杀,由于两人均习惯于单打独斗,所以两人途中不会相遇。为了保护xcj,xcj的迷妹们倾巢而出,占据在地图上的一些格子中,jdr和wly不想打草惊蛇,所以在行进的过程中不会经过有迷妹把守的格子。
已知只有当jdr和wly都到达目标地点才会刺杀成功,问题是xcj有几种死法?(即jdr和wly均顺利到达 (n,m) 的方案数)
【输入格式】
第一行两个整数n,m,表地图的行数和列数。
接下来n行每行m个数,每个数为0或1,0表示 (i,j) 位置上无迷妹,1表有迷妹。
【输出格式】
一个正整数表方案数
题解:
dp+容斥
在格子上乱跑的问题很常见,但这种是比较难的。
首先,如果我们不考虑不能重叠和不能经过的点,那么这个问题很简单,有结论:
这里重点谈一谈如何快速求出某一点到终点的路线数:
首先我们发现,对某一点的路线数dis[i][j],有:
dis[i][j]=dis[i+1][j]+dis[i][j+1](这一点很好理解,每一个点有两种移动方向)
那么这个很像一个杨辉三角的递推式,所以我们可以把整个矩阵看做斜的杨辉三角处理,这样整个矩阵就会变成(以n=4,m=3为例):
C(5,2) C(4,2) C(3,2) C(2,2)
C(4,1) C(3,1) C(2,1) C(1,1)
C(3,0) C(2,0) C(1,0) C(0,0)
那么接下来,我们就可以探索规律了。由于我们只需要处理第一行和第一列的内容,所以我们只研究这两段: 、 首先对第一行:我们发现,第一行所有上面的数是一样的,应为m-1,而下面的数有一个递减的趋势,我们推测要减掉i
那么用什么减掉i呢?经探索规律得,应用m+n-1减掉i即可!
所以第一行第i个数到终点路径数的通项即为:C(m+n-i-1,m-1)(可以思考一下为什么下面一定比上面大,很简单)
同理,第一列第i个数到终点路径的通项即为:C(m+n-i-1,m-i)(这里推导猜想过程完全同上)
当然我们会发现,这题没有这么简单
所以我们回归最原始的dp思想:
仍然保留递推式:
dp[i][j]=dp[i-1][j]+dp[i][j-1]
那么对于无法到达的点,我们仅需令dp=0即可
这样方案数就能推出来了
接下来我们考虑路径不重叠的问题:
首先有一个结论很显然:
如果某一方案是合法的,那么一定是一个人从绿色到绿色,另一个人从蓝色到蓝色!
为什么?
很简单:如果一个人从蓝色到绿色,那另一个人只能从绿色到蓝色,这样一定会在中间有交叉!
所以我们仅需应用一个容斥,将绿->绿,蓝->蓝的方案数相乘减去绿->蓝,蓝->绿的方案数相乘即可
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define mode 5462617
#define ll long long
using namespace std;
ll dp1[2005][2005],dp2[2005][2005];
int used[2005][2005];
int n,m;
int main()
{
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&used[i][j]);
}
}
if(used[1][2]||used[2][1])
{
printf("0\n");
return 0;
}
dp1[1][2]=1,dp2[2][1]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(i==1&&j==1)
{
continue;
}
if(i==1&&j==2)
{
continue;
}
if(i==2&&j==1)
{
continue;
}
if(used[i][j])
{
dp1[i][j]=0;
dp2[i][j]=0;
}else
{
dp1[i][j]=(dp1[i-1][j]+dp1[i][j-1])%mode;
dp2[i][j]=(dp2[i-1][j]+dp2[i][j-1])%mode;
}
}
}
printf("%lld\n",((dp1[n-1][m]*dp2[n][m-1]%mode-dp1[n][m-1]*dp2[n-1][m]%mode)%mode+mode)%mode);
return 0;
}