HZOI20190722 B visit 组合数+CRT合并

题目:https://www.cnblogs.com/Juve/articles/11226266.html

solution:

30%:dp

设dp[k][i][j]表示经过k时间,在(i,j)的方案数

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #define ll long long
 5 #define MAXN 105
 6 using namespace std;
 7 ll dp[MAXN][MAXN<<1][MAXN<<1],t,n,m,mod;
 8 int main(){
 9     scanf("%lld%lld%lld%lld",&t,&mod,&n,&m);
10     if(n<0) n=-n;
11     if(m<0) m=-m;
12     if((t-n-m)&1){
13         cout<<0<<endl;
14         return 0;
15     }
16     int xkl=105;
17     dp[0][xkl][xkl]=1;
18     for(ll i=1;i<=t;i++){
19         for(ll j=-t;j<=t;j++){
20             for(ll k=-t;k<=t;k++){
21                 dp[i][j+xkl][k+xkl]=(dp[i][j+xkl][k+xkl]+dp[i-1][j+xkl][k+1+xkl]+dp[i-1][j+xkl][k-1+xkl]+dp[i-1][j-1+xkl][k+xkl]+dp[i-1][j+1+xkl][k+xkl])%mod;
22             }
23         }
24     }
25     cout<<dp[t][n+xkl][m+xkl]%mod<<endl;
26     return 0;
27 }

它可以走到-t,所以要防爆

正解:(转载Barca)

我们设ri,le,up,down分别为向右左上下走的步数,且总步数为T,

然后我们只要知道,向一个方向走的步数就能得到其他的,但是我们发现光凭一个是求不出的,

我们再转化一下思路,我们设在上下方向走的步数为k,则up+down=k,

然后又因为他最后必须走到(n,m),所以向上走的步数减去向下走的步数为m,即up-down=m

同理我们可以求出ri与le的关系,同样是两个方程,

这样我们就可以通过枚举k,来得到向各个方向走的步数,这样就能列出组合数的式子了,即:

ans=(不知为何我数学公式挂了)

$sum limits_{k=m}^{t-n}C_t^k*C_k^{frac{k-m}{2}}*C_{n-k}^{frac{t-k-n}{2}}$

-------->这有什么错吗?

但你这样也只有60分,因为mod不一定是质数

那我们可以CRT合并

先对mod分解因数,再用lucas求出mod每个因数下的ans,然后发现一个方程组

$egin{cases} x & equiv & ans_1 (mod p_1) \ x & equiv & ans_2 (mod p_2) \ & vdots & \ x & equiv & ans_n (mod p_n) end{cases}$

from Rorschach_XR 为什么今天我Latex挂掉了,rp++14:38它终于好了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
#define MAXN 100005
using namespace std;
ll t,n,m,mod,ans[MAXN];
ll q_pow(ll a,ll b,ll p){
	ll res=1;
	while(b){
		if(b&1) res=(res*a)%p;
		a=(a*a)%p;
		b>>=1;
	}
	return res%p;
}
ll tot=0,prime[MAXN];
void divide(ll p){//分解质因数
	ll t=(ll)sqrt(p);
	for(ll i=2;i<=t;i++){
		if(p%i==0){
			prime[++tot]=i;
			p/=i;
		}
	}
	if(p>1) prime[++tot]=p;
}
ll fac[MAXN];
void get_fac(ll p){
	fac[0]=fac[1]=1;
	for(ll i=2;i<=t;i++) 
		fac[i]=fac[i-1]*i%p;
}
ll C(ll n,ll m,ll p){
	if(m>n) return 0;
	return fac[n]*q_pow(fac[m]*fac[n-m]%p,p-2,p)%p;
}
ll lucas(ll n,ll m,ll p){
	if(m==0) return 1;
	return C(n%p,m%p,p)*lucas(n/p,m/p,p)%p;
}
ll CRT(ll ans[]){
	ll res=0;
	for(ll i=1;i<=tot;i++)
		res=(res+mod/prime[i]*ans[i]%mod*q_pow(mod/prime[i],prime[i]-2,prime[i])%mod)%mod;
	return res;
}
int main(){
	scanf("%lld%lld%lld%lld",&t,&mod,&n,&m);
	if(n<0) n=-n;
	if(m<0) m=-m;
	divide(mod);//分解质因数
	for(ll i=1;i<=tot;i++){
		get_fac(prime[i]);//mod(prime[i])意义下的fac
		for(ll k=n;k<=t-m;k+=2)//步数不能为1
			ans[i]=(ans[i]+lucas(t,k,prime[i])*lucas(k,(k-n)/2,prime[i])%mod*lucas(t-k,(t-m-k)/2,prime[i])%mod)%mod;
	}
	printf("%lld
",CRT(ans)%mod);
	return 0;
}
原文地址:https://www.cnblogs.com/Juve/p/11230593.html