ACM-ICPC 2017 Mid-Central Regional H Hopscotch题解
题目
原题链接
题意
- 给定终点
(N,N)
,X轴,Y轴步距:x,y
,要想向X轴或者Y轴移动,每次移动距离必须至少等于对应的步距才行。要求输出有多少种移动方式,但是题目同时说明,这个数字可能很大,所以要求我们在计算过程中mod(1e9+7)
,输出结果时也需要模。
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1e9+7,maxn=1e6+7;
ll fac[maxn],niyuan[maxn];
long long qpowmod (long long a,long long b,long long mod){
long long result=1;
while (b){
if (b&1)
result=(result*a)%mod;
b>>=1;
a=(a*a)%mod;
}
return (result)%mod;
}
ll inv (ll a,ll mod){
return qpowmod(a, mod-2, mod);
}
ll initial (ll fac[],ll niyuan[]){
fac[0]=1;
for (int i=1;i<maxn;i++){
fac[i]=(fac[i-1]*i)%mod;
}
niyuan[maxn-1]=inv(fac[maxn-1], mod);
for (int i=maxn-2;i>=0;i--){
niyuan[i]=(niyuan[i+1]*(i+1))%mod;
}
return 0;
}
ll C (ll n,ll m,ll fac[],ll niyuan[]){
if (n>=m)
return fac[n]*niyuan[m]%mod*niyuan[n-m]%mod;
else return 0;
}
int main()
{
ll n,x,y;
cin>>n>>x>>y;
ll total=min(n/x,n/y),count=0;
initial(fac, niyuan);
for (ll i=0;i<=total;i++){
count=(count+C(n-i*(x-1)-1,i-1,fac,niyuan)%mod*C(n-i*(y-1)-1,i-1,fac,niyuan)%mod)%mod;
}
cout<<count<<endl;
return 0;
}
思路
- 这题类似于以前的求有多少种分配方式,不过以前是只有一个需要求,而这里有两个,一个x,一个y,这两种方式对应的方法数求出来之后,将两个数相乘即为此时的总方法数,但是每次至少移动一个x或者y,而一次性可以移动
N/y或者N/x
个x或者y,因为不能超过N,所以必须取这两个值中的最小值。所以我们只需要分类讨论,把一次移动长度从一个x到N个x所有的情况都加在一起,同时在计算过程中按照题目要求取模即可,要求对应的情况,我们需要使用高中学习的组合数。 - 本题数据量巨大,不取模不行。而且题目也要求取1e9+7的模。所以本题肯定要用到快速幂/积取模。
- 组合数取模要用到逆元和费马小定理,逆元的概念为:M*(N!%P)=1,M就是N!%P的逆元。
- 根据费马小定理,求取逆元简单表述(
其实是我也不是很懂怎么证明)如下:若m!%p,它的逆元是(m!)(p-2)。维基百科上的费马小定理 - 求解方法:
由
C
n
m
C_n^m
Cnm%P
- 先算出n!%p、m!%p、(n-m)!%p,用fac[i]表示 i!%p 的值
- 因为组合数取模是(n!)/(m!(n-m)!)%p,因此需要计算出m!%p、(n-m)!%p的逆元,根据费马小定理,m!%p、(n-m)!%p的逆元分别是(m!)(p-2)、((n-m)!)(p-2)
- 快速幂取模求出(m!)(p-2)%p、((n-m)!)(p-2)%p,分别记录下来,假设标为M和NM
- 最后求一下(n!)%p * M* NM % p
- 由以上思路,我们打表,将1-(1e6+7)范围内所有组合数和逆元都算出来(计算中就得取模),在按照以上描述,将取模之后的阶乘算出,再相乘,累加,过程中别忘记取模。
总结
- 本题对于现在的我来说,算是难题,本体的思路比较清晰,就是要用到组合数取模我不是很会,百度了之后,才有所了解。