zoukankan      html  css  js  c++  java
  • 题(problem)(详解10.5hu测T3:Catalan)

    @liu_runda
    【题目描述】
    出个题就好了.这就是出题人没有写题目背景的原因.
    你在平面直角坐标系上.
    你一开始位于(0,0).
    每次可以在上/下/左/右四个方向中选一个走一步.
    即:从(x,y)走到(x,y+1),(x,y-1),(x-1,y),(x+1,y)四个位置中的其中一个.
    允许你走的步数已经确定为n.现在你想走n步之后回到(0,0).但这太简单了.你希望知道有多少种不同的方案能够使你在n步之后回到(0,0).当且仅当两种方案至少有一步走的方向不同,这两种方案被认为是不同的.
    答案可能很大所以只需要输出答案对10^9+7取模后的结果.(10^9+7=1000000007,1和7之间有8个0)
    这还是太简单了,所以你给能够到达的格点加上了一些限制.一共有三种限制,加上没有限制的情况,一共有四种情况,用0,1,2,3标号:

    1. 0.没有任何限制,可以到达坐标系上所有的点,即能到达的点集为{(x,y)|x,y为整数}
    2. 只允许到达x轴非负半轴上的点.即能到达的点集为{(x,y)|x为非负数,y=0}
    3. 只允许到达坐标轴上的点.即能到达的点集为{(x,y)|x=0或y=0}
    4. 只允许到达x轴非负半轴上的点,y轴非负半轴上的点以及第1象限的点.即能到达的点集为{(x,y)|x>=0,y>=0}

    【输入格式】
    一行两个整数(空格隔开)n和typ,分别表示你必须恰好走的步数和限制的种类.typ的含义见【题目描述】.
    【输出格式】
    一行一个整数ans,表示不同的方案数对10^9+7取模后的结果.

    【样例输入0】
    100 0
    【样例输出0】
    383726909

    【样例输入1】
    100 1
    【样例输出1】
    265470434

    【样例输入2】
    100 2
    【样例输出2】
    376611634

    【样例输入3】
    100 3
    【样例输出3】
    627595255

    【数据范围】
    10%的数据,typ=0,n<=100
    10%的数据,typ=0,n<=1000
    5%的数据, typ=0,n<=100000
    10%的数据,typ=1,n<=100
    10%的数据,typ=1,n<=1000
    5%的数据, typ=1,n<=100000
    10%的数据,typ=2,n<=100
    15%的数据,typ=2,n<=1000
    10%的数据,typ=3,n<=100
    10%的数据,typ=3,n<=1000
    5%的数据, typ=3,n<=100000
    以上11部分数据没有交集.
    100%的数据,保证n为偶数,2<=n<=100000,0<=typ<=3.

    分析:
    考场上的dp做法详见hu测10.5

    正解:
    四合一Catalan应用

    first.typ=1

    裸的Catalan

    模拟出入栈

    second.typ=0

    对于typ=0的数据
    枚举横向移动了多少步
    横向移动i步时(为了存在合法解,i必须是偶数)
    方案数为C(n,i)*C(i,i/2)*C((n-i),(n-i)/2)

    横向选择i步移动,i步中有一半正向移动,剩下纵向的n-i步中,一半正向移动

    third.typ=2

    f[i]表示走了i步回到原点的方案数
    (中途可能回到过原点多次)
    枚举第一次回到原点时走过的步数j(为了存在合法解,j为偶数)
    则此时方案数为f[i-j]*catalan(j/2-1)

    解释:
    第j步第一次回到远点,之后可能有多次再次回原点(f[i-j]),
    在计算这j步时,我们必须保证j步中不回原点,所以catalan(j/2-1)
    这个-1就是保证栈不为空
    (在我们眼里,移动就像出入栈)

    最后答案需要*4
    复杂度为O(n^2)所以最大范围只出到1000

    fourth.typ=3

    枚举横向移动了多少步
    横向移动i步时(为了存在合法解,i必须是偶数)
    方案数为C(n,i)*catalan(i/2)*catalan((n-i)/2)

    选择n步中的i步横向移动,其中一半的步数正向移动,
    剩下纵向移动的步数中,也有一半的步数正向移动
    Catalan保证是在第一象限内移动(任意前缀入栈不少于出栈

    //这里写代码片
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define ll long long
    
    using namespace std;
    
    const ll mod=1000000007;
    const int N=200010;
    int n,typ;
    ll inv[N],jc[N],f[1010]; 
    
    ll KSM(ll a,int b)
    {
        a%=mod;
        ll t=1;
        while (b)
        {
            if (b&1) 
               t=(t%mod*a%mod)%mod;
            b>>=1;
            a=(a%mod*a%mod)%mod;
        }
        return t%mod;
    }
    
    void cl()
    {
        jc[0]=1; jc[1]=1;
        for (int i=2;i<N;i++) jc[i]=(jc[i-1]%mod*(ll)i%mod)%mod;
        inv[0]=1; inv[1]=1;
        for (int i=2;i<N;i++) inv[i]=((mod-mod/i)%mod*inv[mod%i]%mod)%mod;
        for (int i=1;i<N;i++) inv[i]=(inv[i]*(ll)inv[i-1])%mod;  //阶乘的逆元 
    }
    
    ll C(int n,int m)
    {
        if (n<m) return 0;
        if (m==0) return 1;
        return (jc[n]%mod*inv[m]%mod*inv[n-m]%mod)%mod;
    }
    
    ll Catalan(int x)  //公式求法 
    {
        return C(2*x,x)%mod*KSM((ll)(x+1),mod-2)%mod;   //费马小定理求逆元 
    }
    
    int main()
    {
        freopen("problem.in","r",stdin);
        freopen("problem.out","w",stdout);
        scanf("%d%d",&n,&typ);
        cl();
        if (typ==1)
        {
            printf("%lld",Catalan(n/2));
        }
        else if (typ==0)
        {
            ll ans=0; 
            for (int i=0;i<=n;i+=2)   //为了能回到原点,枚举的横向步数一定是偶数 
                ans=(ans+C(n,i)%mod*C(i,i/2)%mod*C(n-i,(n-i)/2)%mod)%mod;
            printf("%lld",ans%mod);
        }
        else if (typ==2)
        {
            n/=2;  //方便dp 
            f[0]=1;
            f[1]=4;
            for (int i=2;i<=n;i++)   //枚举总步数 
                for (int j=1;j<=i;j++)   //枚举第一次到达原点的步数
                    f[i]=(f[i]%mod+f[i-j]%mod*4*Catalan(j-1)%mod)%mod;
            printf("%lld",f[n]%mod);
        }
        else
        {
            ll ans=0;
            for (int i=0;i<=n;i+=2)
                ans=(ans+C(n,i)%mod*Catalan(i/2)%mod*Catalan((n-i)/2)%mod)%mod;
            printf("%lld",ans);
        }
        return 0;
    }
  • 相关阅读:
    XGBoost算法--学习笔记
    机器学习--学习书籍
    一天搞懂深度学习-深度学习新浪潮
    如何在 Office 365 环境中设置联机 Exchange 邮箱大小和限制
    玩转Office 365中的Exchange Online服务 之十一 怎样在Exchange Online中配置邮件传递限制《转》
    玩转Office 365中的Exchange Online服务 之六 了解Exchange Online对于邮箱使用的限制《转》
    Hyper-V Ubuntu修改分辨率
    k8s 集群基本概念<转>
    Azure 中 Linux VM 的 SSH 公钥和私钥对
    docker学习笔记(k8s) 《转》
  • 原文地址:https://www.cnblogs.com/wutongtong3117/p/7673092.html
Copyright © 2011-2022 走看看