visit 组合数取模

  题面依旧保密。。。。

  我们可以知道,这个题的不一样之处在于在网格中的步数可能远远多于需要,那么我们应该怎么做呢?考场上的思路是:首先选出来n+m步让我能走到终点,然后在去找多余的步数。然后就不会了,算重一大堆。。考后听skyh讲题的时候发现其实和最基础的方法类似,这个题的基础版是$T=n+m$,那么方案肯定是$C_{n+m}^n$,它的思想是一定走n+m步,选n步向上走,剩下向右走,一开始还纠结为什么不是,$C_{n+m}^n*C_{n+m}^m$,其实最一开始应该是$C_{n+m}^n*C_{m}^m$。那么继续这个思路,在我们T步的时候,什么时候是确定的?发现如果T-(n+m)是奇数,那么显然是死掉的。然后考虑选步,和之前一样,在观察的时候发现当上下方向的步数确定的时候,左右方向也确定,因为不能斜着走,那么可以得到,当枚举上下方向步数,再选出向上步数,剩下向下走,左右也一样。

  于是我们就愉快的得到了$\sum\limits_{k=n,k+=2}^{T-m}C_{T}^{k}*C_{k}^{(k-n)/2}*C_{T-k}^{(T-k-m)/2}$.上下左右一定得走够n和m,那么就有k的范围,因为奇数一定走不到(多走偶数次才相当于没走),k一定是+2变换的,这样能保证k-n一定是偶数,k-n是上下方向上多余的步数,一定是走过去又走回来,除以2就是向下走的。然后左右同理,值得一提的就是在左右走的时候,T-k-m是左右走的废步,它也应该是偶数,证明很容易,k和n的奇偶性相同,那么T-k-m和T-n-m奇偶性相同,T-n-m一定是奇数已经死了,那这个肯定是偶数。于是这个题就愉快的解决了,然后看到输入你又会点进来,没错,模非质数。。。

  当会exlucas大神已经可以去码了,还有一个蒟蒻做法,毕竟这只是个NOIP模拟,题目给的数也不是随意的,它是一个所谓的什么square-free-number,就是质因数的最高次只有一次,那这完全可以用unexlucas。。。也就是lucas+CRT解决。

  设答案为ans,将模数分解质因数,那么就可以得到p1,p2......,我们求出ans对每一个p的余数,最终用中国剩余定理合并就知道了ans的最小值即ans%mod

  

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int N=1000020;
int p[N],w[N],tt;
int fac[N],inv[N];
long long rd()
{
    long long s=0;
    char cc=getchar();
    while(cc<'0'||cc>'9') cc=getchar();
    while(cc>='0'&&cc<='9') s=(s<<3)+(s<<1)+cc-'0',cc=getchar();
    return s;
}
const long long t=rd(),mod=rd(),n=rd(),m=rd();
inline long long qpow(int a,int k,int p)
{
    long long ans=1;
    for(;k;k>>=1,a=1ll*a*a%p) if(k&1) ans=1ll*ans*a%p;
    return ans;
}
inline long long C(int n,int m,int p)
{
    if(n<m) return 0;
    return 1ll*fac[n]*inv[m]%p*inv[n-m]%p;
}
inline void cut(int n)
{
    for(register int i=2;1ll*i*i<=n;i++)
        if(n%i==0)
            p[++tt]=i,n/=i;
    if(n>1)p[++tt]=n;
    //return tt==1;
}
long long lucas(int n,int m,int p)
{
    if(m==0) return 1;
    return C(n%p,m%p,p)*lucas(n/p,m/p,p)%p;
}
inline long long crt()
{
    long long ans=0;
    for(register int i=1;i<=tt;i++) ans=(ans+(mod/p[i])*qpow(mod/p[i],p[i]-2,p[i])%mod*w[i]%mod)%mod;
    return ans;
}
void get_fac(int p)
{
    register int mm=min(1ll*p-1,t);
    fac[0]=1;
    for(register int i=1;i<=mm;i++) fac[i]=1ll*fac[i-1]*i%p;
    inv[mm]=qpow(fac[mm],p-2,p);
    for(register int i=mm;i>0;i--) inv[i-1]=1ll*inv[i]*i%p;
}
int main()
{
    cut(mod);
    for(register int i=1;i<=tt;i++)
    {
        get_fac(p[i]);
        for(register int k=n;k<=t-m;k+=2)
            w[i]=(w[i]+lucas(t,k,p[i])*lucas(k,(k-n)/2,p[i])%p[i]*lucas(t-k,(t-k-m)/2,p[i])%p[i])%p[i];
    }
    printf("%lld\n",crt());
}
View Code

猜你喜欢

转载自www.cnblogs.com/starsing/p/11235539.html
今日推荐