noip 模拟赛之xcj的妹子

【题目背景】

众所周知,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;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NOI(全国青少年信息学奥林匹克竞)模拟的测试数据是指用于评测参选手的程序的输入和对应的输出。测试数据是非常重要的,因为它决定了参选手的程序能否正确地解决问题。 在NOI模拟中,测试数据具有以下特点: 1.充分覆盖:测试数据应涵盖各种可能的输入情况,包括边界条件和极端情况。通过提供不同的测试数据,可以考察选手对问题的全面理解和解决能力。 2.随机性和均衡性:为了公平起见,测试数据应该是随机生成的,而不是针对某个特定算法或解法设计的。同时,测试数据应该是均衡的,即各种情况的概率应该大致相等,以避免偏向某些解法。 3.合理性和可行性:测试数据应该是合理和可行的,即符合题目要求的输入数据,并且是选手能够通过编写程序来处理的。测试数据应该考虑到程序的限制和时间复杂度,以充分测试选手的编程能力。 NOI模拟的测试数据通常由经验丰富的考题组负责生成。他们会根据题目的要求和限制,设计出一组合理、充分、随机和均衡的测试数据,以确保参选手的程序在各种情况下都能正确运行,并且能通过性能测试。 总之,测试数据在NOI模拟中起到了至关重要的作用,它既考察了选手对问题的理解和解决能力,又提高了选手编程的技巧和效率。同时,合理和恰当的测试数据也是公平竞的保证,确保每个参选手有相同的机会和条件进行竞争。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值