纪中暑假集训 2020.08.14【NOIP提高组】模拟 T2:【GDOI2014模拟】网格

【GDOI2014模拟】网格

Description

某城市的街道呈网格状,左下角坐标为A(0, 0),右上角坐标为B(n, m),其中n >= m。现在从A(0, 0)点出发,只能沿着街道向正右方或者正上方行走,且不能经过图示中直线左上方的点,即任何途径的点(x, y)都要满足x >= y,请问在这些前提下,到达B(n, m)有多少种走法。
在这里插入图片描述

Input

输入文件中仅有一行,包含两个整数n和m,表示城市街区的规模。

Output

输出文件中仅有一个整数和一个换行/回车符,表示不同的方案总数。

Sample Input

输入1:

6 6

输入2:

5 3

Sample Output

输出1:

132

输出2:

28

Data Constraint

50%的数据中,n = m,在另外的50%数据中,有30%的数据:1 <= m < n <= 100

100%的数据中,1 <= m <= n <= 5 000

反思&题解

比赛思路: 刚看以为是送分题,果断DP,设 f [ i ] [ j ] f[i][j] 表示走到点 ( i , j ) (i,j) 时总共的步数,显然方程为: f [ i ] [ j ] = f [ i 1 ] [ j ] + f [ i ] [ j 1 ] f[i][j]=f[i-1][j]+f[i][j-1] ;后来看了一下空间,WOC才65536KB,不过开个滚动马上解决,结果测了个极限数据,发现unsigned long long也会爆,之后打了个高精度,过了样例,信心满满地测了个极限数据,结果T飞了……后来发现当 n = m / n = m + 1 n=m/n=m+1 时答案就是卡特兰数的第n位,但是因为我不会 O ( n ) O(n) 构造卡特兰数,于是拿不到特殊数据的50分……
正解思路: 根据一堆图示推算可以得到答案为: C n + m m C n + m m 1 C^m_{n+m}-C^{m-1}_{n+m} ,详细推算过程见这篇大佬的博客(我太懒了就不写在这了)
其实直接套这个式子打高精度就能过了,但我相信一定有朋友跟我一样不想打高精度除法,那么现在我来讲一种好的方法:
首先我们将原式化简一下:
C n + m m C n + m m 1 C^m_{n+m}-C^{m-1}_{n+m}
= ( n + m ) ! m ! n ! ( n + m ) ! ( m 1 ) ! ( n + 1 ) ! =\dfrac{(n+m)!}{m!*n!}-\dfrac{(n+m)!}{(m-1)!(n+1)!}
= ( n + m ) ! ( n + 1 ) ! m ! ( n + 1 ) ( n + m ) ! m m ! ( n + 1 ) ! =\dfrac{(n+m)!(n+1)!}{m!(n+1)}-\dfrac{(n+m)!m}{m!(n+1)!}
= ( n + m ) ! ( n + 1 m ) m ! ( n + 1 ) ! =\dfrac{(n+m)!(n+1-m)}{m!(n+1)!}
好的,到这里就够了,看到了一条除号(分数线),这时候我们肯定会下意识想到分解质因数!因为答案肯定是一个整数,所以这个式子的分母的所有质因数肯定分子里面也有(这个不难证明吧),所以我们只需要将分子的每一项先分解质因数,之后再把分母的每一项分解质因数,去重之后乘上没有重复的质因数就行了
反思: 数学思维要加强,有时候可以画图找找规律,不过我觉得以初一中游水平的数学考场上想出这题还是有(gen)很(ben)大(bu)很(ke)难(neng)度(de)

CODE

#include<bits/stdc++.h>
using namespace std;
int zyz[10005],n,m,tot,ans[10005];
void cheng(int num)
{
	int x=0,i;
	for (i=1;i<=ans[0];i++)
	{
		ans[i]=ans[i]*num+x;
		x=ans[i]/10;
		ans[i]%=10;
	}
	while (x>0)
	{
		ans[++ans[0]]=x%10;
		x/=10;
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	int i;
	int n1=n-m+1,n2=n1;
	for (i=2;i<=n2;i++)
	{
		if (n1==1) break;
		while (n1%i==0)
		{
			zyz[i]++;
			n1/=i;
		}
	}
	int j;
	for (j=1;j<=m+n;j++)
	{
		n1=j;
		n2=n1;
		for (i=2;i<=n2;i++)
		{
			if (n1==1) break;
			while (n1%i==0)
			{
				zyz[i]++;
				n1/=i;
			}
		}
	}
	for (j=1;j<=m;j++)
	{
		n1=j;
		n2=n1;
		for (i=2;i<=n2;i++)
		{
			if (n1==1) break;
			while (n1%i==0)
			{
				zyz[i]--;
				n1/=i;
			}
		}
	}
	for (j=1;j<=n+1;j++)
	{
		n1=j;
		n2=n1;
		for (i=2;i<=n2;i++)
		{
			if (n1==1) break;
			while (n1%i==0)
			{
				zyz[i]--;
				n1/=i;
			}
		}
	}
	ans[0]=1;
	ans[1]=1;
	for (i=2;i<=n+m;i++)
	{
		if (zyz[i]>0)
		{
			for (j=1;j<=zyz[i];j++)
				cheng(i);
		}
	}
	for (i=ans[0];i>=1;i--)
		printf("%d",ans[i]);
	printf("\n");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/CMC_YXY/article/details/108012306