[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} \binom{t}{i} \binom{i}{\frac{i-n}{2}} \binom{t-i}{\frac{t-i-m}{2}}$

(从天皇那里copy过来的)

按道理讲本题应该结束了

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

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

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

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

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

$\begin{cases} x\equiv ans_1(mod\ p_1)\\ x\equiv ans_2(mod\ p_2)\\ x\equiv ans_3(mod\ p_3)\\ ...\\ x\equiv 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NOI(全国青少年信息学奥林匹克竞赛)模拟赛的测试数据是指用于评测参赛选手的程序的输入和对应的输出。测试数据是非常重要的,因为它决定了参赛选手的程序能否正确地解决问题。 在NOI模拟赛中,测试数据具有以下特点: 1.充分覆盖:测试数据应涵盖各种可能的输入情况,包括边界条件和极端情况。通过提供不同的测试数据,可以考察选手对问题的全面理解和解决能力。 2.随机性和均衡性:为了公平起见,测试数据应该是随机生成的,而不是针对某个特定算法或解法设计的。同时,测试数据应该是均衡的,即各种情况的概率应该大致相等,以避免偏向某些解法。 3.合理性和可行性:测试数据应该是合理和可行的,即符合题目要求的输入数据,并且是选手能够通过编写程序来处理的。测试数据应该考虑到程序的限制和时间复杂度,以充分测试选手的编程能力。 NOI模拟赛的测试数据通常由经验丰富的考题组负责生成。他们会根据题目的要求和限制,设计出一组合理、充分、随机和均衡的测试数据,以确保参赛选手的程序在各种情况下都能正确运行,并且能通过性能测试。 总之,测试数据在NOI模拟赛中起到了至关重要的作用,它既考察了选手对问题的理解和解决能力,又提高了选手编程的技巧和效率。同时,合理和恰当的测试数据也是公平竞赛的保证,确保每个参赛选手有相同的机会和条件进行竞争。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值