zoukankan      html  css  js  c++  java
  • bzoj3782上学路线(Lucas+CRT+容斥DP+组合计数)

    传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=3782

    有部分分的传送门:https://www.luogu.org/problemnew/show/P4478

    看到标题开始还以为是AHOI的小雪和小可可……

    题解:乍一看会40pts:测试点1、2:n,m<=1000的直接O(nm)DP;测试点3、4:没有障碍物直接C(n+m,n),然后p=1e6+3是质数可以直接取模。

    想了几分钟会60pts:测试点5、6:模数可以拆成几个不超过1e5的质数的乘积,直接算出C(n+m,n)对每个质数的模数,然后CRT合并一下就行了。

    不会CRT的左转,我原来也是看这个博客学的:https://blog.csdn.net/niiick/article/details/80229217

    其实满分也很可做,容斥一下就行了:把障碍物按x从小到大,x相同按y从小到大排序,然后f[i]表示不经过前(i-1)个障碍物但经过第i个障碍物的方案,然后增加最后一个点为(n,m),然后可以计算f[i]=C(x[i]+y[i],y[i])+Σf[j]C(x[i]-x[j]+y[i]-y[j],x[i]-x[j]),其中j满足x[j]<=x[i]&&y[j]<=y[i],这个计算由于T<=200算组合数+CRT合并也不会超时,复杂度O(T^2log)。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1e6+7;
    struct node{ll x,y;}a[N];
    ll n,m,P,f[N],p[8],fac[6][N],inv[6][N],mul[8],imul[8],g[8];
    int num,tp;
    bool cmp(node a,node b){return a.x==b.x?a.y<b.y:a.x<b.x;}
    ll qpow(ll a,ll b,ll p)
    {
        ll ret=1;
        while(b)
        {
            if(b&1)ret=ret*a%p;
            a=a*a%p,b>>=1;
        }
        return ret;
    }
    ll c(ll a,ll b,int i)
    {
        if(a<b)return 0;
        if(a<p[i]&&b<p[i])return fac[i][a]*inv[i][b]%p[i]*inv[i][a-b]%p[i];
        return c(a%p[i],b%p[i],i)*c(a/p[i],b/p[i],i)%p[i];
    }
    ll C(ll a,ll b)
    {
        if(!tp)return c(a,b,0);
        ll ret=0;
        for(int i=1;i<=4;i++)g[i]=c(a,b,i);
        for(int i=1;i<=4;i++)ret=(ret+g[i]*mul[i]%P*imul[i]%P)%P;
        return ret;
    }
    int main()
    {
        scanf("%lld%lld%d%lld",&n,&m,&num,&P);
        for(int i=1;i<=num;i++)scanf("%lld%lld",&a[i].x,&a[i].y);
        a[++num]=(node){n,m};
        sort(a+1,a+num+1,cmp);
        if(P==1e6+3)p[0]=1e6+3;else p[1]=3,p[2]=5,p[3]=6793,p[4]=10007,tp=1;
        if(tp)
        {
            for(int i=1;i<=4;i++)
            {
                mul[i]=P/p[i],imul[i]=qpow(mul[i],p[i]-2,p[i]);
                fac[i][0]=1;for(int j=1;j<p[i];j++)fac[i][j]=fac[i][j-1]*j%p[i];
                inv[i][p[i]-1]=qpow(fac[i][p[i]-1],p[i]-2,p[i]);
                for(int j=p[i]-1;j;j--)inv[i][j-1]=inv[i][j]*j%p[i];
            }
        }
        else{
            fac[0][0]=1;for(int i=1;i<P;i++)fac[0][i]=fac[0][i-1]*i%P;
            inv[0][P-1]=qpow(fac[0][P-1],P-2,P);
            for(int i=P-1;i;i--)inv[0][i-1]=inv[0][i]*i%P;
        }
        for(int i=1;i<=num;i++)
        {
            f[i]=C(a[i].x+a[i].y,a[i].x);
            for(int j=1;j<i;j++)if(a[j].x<=a[i].x&&a[j].y<=a[i].y)
            f[i]=(f[i]-f[j]*C(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x)%P+P)%P;
        }
        printf("%lld",f[num]);
    }
    View Code
  • 相关阅读:
    svn命令
    Lambda 表达式
    JAVA 探究NIO
    JAVA I/O系统
    MySQL 锁
    spring boot 集成 zookeeper 搭建微服务架构
    架构演化
    JAVA 集合
    spring boot 发送邮件
    MySQL 隔离级别
  • 原文地址:https://www.cnblogs.com/hfctf0210/p/10428351.html
Copyright © 2011-2022 走看看