zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    洛谷1894 [USACO4.2]完美的牛栏The Perfect Stall
    洛谷2417 课程
    洛谷2860 [USACO06JAN]冗余路径Redundant Paths
    洛谷1983 车站分级
    BZOJ1178或洛谷3626 [APIO2009]会议中心
    BZOJ1179或洛谷3672 [APIO2009]抢掠计划
    CF Round #516 (Div. 2, by Moscow Team Olympiad)
    洛谷1262 间谍网络
    NOI导刊 2018河南郑州游记
    BZOJ1001或洛谷4001 [BJOI2006]狼抓兔子
  • 原文地址:https://www.cnblogs.com/Juve/p/11230593.html
Copyright © 2011-2022 走看看