【GDOI2014模拟】JZOJ2020年8月14日T2 网格

【GDOI2014模拟】JZOJ2020年8月14日T2 网格

题目

Time and Memory Limits

在这里插入图片描述

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

题解

题意

给出一个笛卡尔坐标系
问在满足任何途径的点 ( x , y ) (x,y) (x,y)都满足 x ≥ y x≥y xy的情况下,从 ( 0 , 0 ) (0,0) (0,0)走到 ( n , m ) (n,m) (n,m)有多少种走法

分析

注意到 n , m n,m n,m都是5000的,而且空间很小
普通的 n ∗ m n*m nm暴力 D P DP DP不可以
发现有个要求
找规律发现
A n s = C n + m m − C n + m m − 1 Ans=C_{n+m}^m-C_{n+m}^{m-1} Ans=Cn+mmCn+mm1
那么高精度安排即可
但是又要打减、乘、除,十分麻烦
尝试化简
a = n + m a=n+m a=n+m, b = m b=m b=m
A n s = C n + m m − C n + m m − 1 = C a b − c a b − 1 = a ! b ! ( a − b ) ! − a ! ( b − 1 ) ! ( a − b + 1 ) ! = a ! ( a − b + 1 ) b ! ( a − b + 1 ) ! − a ! b b ! ( a − b + 1 ) ! = a ! ( a − 2 b + 1 ) b ! ( a − b + 1 ) ! = ( n + m ) ! ( n − m + 1 ) m ! ( n + 1 ) ! Ans=C_{n+m}^m-C_{n+m}^{m-1}\\=C_a^b-c_a^{b-1}\\=\dfrac{a!}{b!(a-b)!}-\dfrac{a!}{(b-1)!(a-b+1)!}\\=\dfrac{a!(a-b+1)}{b!(a-b+1)!}-\dfrac{a!b}{b!(a-b+1)!}\\=\dfrac{a!(a-2b+1)}{b!(a-b+1)!}\\=\dfrac{(n+m)!(n-m+1)}{m!(n+1)!} Ans=Cn+mmCn+mm1=Cabcab1=b!(ab)!a!(b1)!(ab+1)!a!=b!(ab+1)!a!(ab+1)b!(ab+1)!a!b=b!(ab+1)!a!(a2b+1)=m!(n+1)!(n+m)!(nm+1)
那么就可以质因数分解然后相乘即可

Code

#include<bits/stdc++.h>
#define rg register
using namespace std;
long long n,m,x,tot[100005],ans[1000005];
bool b[100005];
inline long long read()
{
	long long res=0;char ch;
	ch=getchar();
	while (ch<'0'||ch>'9') ch=getchar();
	while (ch>='0'&&ch<='9')
	{
		res=(res<<1)+(res<<3)+(ch-'0');
		ch=getchar();
	}
	return res;
}
int main()
{
	n=read();m=read();
	memset(b,true,sizeof(b));
	b[1]=false;
	for (rg long long i=2;i<=100005;i++)
		for (rg long long j=2;j*i<=100005;j++)
			b[i*j]=false;
	x=n+1-m;
    for (rg long long i=2;i*i<=x;i++)
    {
        if (b[i]==true&&x%i==0)
        {
            while (x%i==0)
            {
                tot[i]++;
                x/=i;
            }
        }
    }
    if (x>1) tot[x]++;
    for (rg long long j=2;j<=n+m;j++)
    {
    	long long k=j;
		for (rg long long i=2;i*i<=k;i++)
		{
			if (b[i]==true&&k%i==0)
			{
				while (k%i==0)
				{
					tot[i]++;
					k/=i;
				}
			}
		}
		if (k>1) tot[k]++;
	}
    for (rg long long j=2;j<=m;j++)
    {
    	long long k=j;
    	for (rg long long i=2;i*i<=k;i++)
    	{
    		if (b[i]==true&&k%i==0)
    		{
    			while (k%i==0)
    			{
    				tot[i]--;
    				k/=i;
				}
			}
		}
		if (k>1) tot[k]--;
	}
	for (rg long long j=2;j<=n+1;j++)
	{
		long long k=j;
		for (rg long long i=2;i*i<=k;i++)
    	{
    		if (b[i]==true&&k%i==0)
    		{
    			while (k%i==0)
    			{
    				tot[i]--;
    				k/=i;
				}
			}
		}
		if (k>1) tot[k]--;
	}
	ans[0]=1;
	ans[1]=1;
	for (rg long long i=1;i<=100000;i++)
	{
		if (b[i]==true&&tot[i]>0)
		{
			for (rg long long j=1;j<=tot[i];j++)
			{
				for (rg long long k=1;k<=ans[0];k++)
					ans[k]*=i;
				x=0;
				for (rg long long k=1;k<=ans[0];k++)
				{
					ans[k]+=x;
					x=ans[k]/10;
					ans[k]%=10;
				}
				ans[ans[0]+1]=x;
				while (ans[ans[0]+1])
				{
					ans[0]++;
					ans[ans[0]+1]=ans[ans[0]]/10;
					ans[ans[0]]%=10;
				}
			}
		}
	}
	for (rg long long i=ans[0];i>=1;i--)
		printf("%lld",ans[i]);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值