[NOIP模拟测试7]visit 题解(组合数学+CRT+Lucas定理)

Orz

因为有T的限制,所以不难搞出来一个$O(T^3)$的暴力dp

但我没试 据说有30分?

正解的话显然是组合数学啦

首先$n,m$可能为负,但这并没有影响,

我们可以都把它搞成正的 即都看作向右上方走

那么可以想到真正有效的步都是向右或者向上走的 其它两个方向都是在起反作用

设u为向上走步数,d下,l左,r右

它们满足关系:

$r-l=m,u-d=n,T=u+d+l+r$

因为有效步数为$m+n$,所以$T-m-n$必为偶数

因为要保证剩下的步上下均分,左右均分

枚举$udlr$其中一个可得最终答案:

$ans=sum limits_{i=n,2|(i-n)}^{t-m} inom{t}{i} inom{i}{frac{i-n}{2}} inom{t-i}{frac{t-i-m}{2}}$

(从天皇那里copy过来的)

按道理讲本题应该结束了

但丧心病狂的出题人还要恶心你一下:模数可能不为质数

但也给出一定是几个互不相同的质数之积,从exLucas的魔爪中拯救了我们

之后就中国剩余定理就行了

把模数分解出质因子$p[i]$,对于每个因子都算一边答案,记为$ans[i]$

那么得到最后答案的过程,就相当于求解

$egin{cases} xequiv ans_1(mod p_1)\ xequiv ans_2(mod p_2)\ xequiv ans_3(mod p_3)\ ...\ xequiv ans_n(mod p_n)\ end{cases}$

这不裸的CRT么?板子打一遍就完事了。

扩欧都不用打,可以用前边的快速幂 结合费马小定理 解CRT里的同余方程。

//#define R
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<cmath>
#define re register
using namespace std;
const int N=100005;
typedef long long ll;
int T,mod,n,m;
ll fac[N],ans[1005];
vector<int> fact;
int abss(int x)
{
    return x<0?-x:x;
}
void divi(int x)
{
    int sq=sqrt(x)+1;
    for(int i=2;i<=sq;i++)
    {
        if(x%i==0)
        {
            fact.push_back(i);
            while(x%i==0)x/=i;
        }
        if(x==1)break;
    }
    if(x!=1)fact.push_back(x);
}
ll qpow(ll a,ll b,ll p)
{
    ll res=1;a=a%p;
    while(b)
    {
        if(b&1)res=res*a%p;
        a=a*a%p;
        b>>=1;
    }
    return res;
}
ll C(ll x,ll y,ll p)
{
    if(x<y)return 0;
    return fac[x]*qpow(fac[y],p-2,p)%p*qpow(fac[x-y],p-2,p)%p;
}
ll lucas(ll x,ll y,ll p)
{
    if(!y)return 1;
    return C(x%p,y%p,p)*lucas(x/p,y/p,p)%p;
}
void getfac(ll p)
{
    fac[0]=1;
    for(int i=1;i<=T;i++)
        fac[i]=1LL*i*fac[i-1]%p;
}
int main()
{
    scanf("%d%d%d%d",&T,&mod,&n,&m);
    n=abss(n),m=abss(m);
    divi(mod);
    int sz=fact.size();
    for(re int now=0;now<sz;now++)
    {
        int p=fact[now];getfac(p);
        for(re int i=n;i<=T-m;i++)
        {
            if((i-n)&1||(T-i-m)&1)continue;
            ll res=1;
            res=res*lucas(T,i,p)%p*lucas(i,(i-n)/2,p)%p*lucas(T-i,(T-i-m)/2,p)%p;
            ans[now]=(ans[now]+res)%p;
        }
    }
    ll anss=0;
    for(re int i=0;i<sz;i++)
    {
        ll times=mod/fact[i];
        ll ress=qpow(times,fact[i]-2,fact[i]);
        ress=(ress%fact[i]+fact[i])%fact[i];
        anss=(anss+times*ress*ans[i])%mod;
    }
    cout<<(anss+mod)%mod<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/Rorschach-XR/p/11230688.html