zoukankan      html  css  js  c++  java
  • 暑假考试题6:problem 题(Catlan + dp + 组合数)

    题目:

    分析:

    若没有限制地在坐标轴上走,无论怎么走,走到的最远的点映射在横纵坐标上的数量是一定的,那么可以枚举这个数量,进行计算。

    而规定只能走到非负半轴的限制,将1看做向右走,-1看做向左走,可以转换成任何时候的前缀和非0,这就对应着括号序

    0限制:坐标轴内都可以随便走 ,无论怎么走, 横坐标走到的最远的点一定是确定的, 且一定要走回来, 纵坐标也一样。
                 选i步走横坐标, 剩下的n-i步走纵坐标, 那么i/2步必须走回去 纵坐标也一样 。

    1限制:为了不走到负半轴 ,就必须每个时刻向右走的步数比向左走多, 即为括号序。

    2限制:定义f[i]为走了i步回到起点的方案数 ,因为必须走到起点才可以走纵坐标 。
                枚举j表示第一次走回到起点时走过的步数 即之前都没有回到过 所以是catlan( j/2 -1 )*4 (有四个方向
     -1原因:为了保证j是第一次走到起点,而没有在中途回到起点,就要保证第一个括号和最后一个括号匹配,那么第一个括号就是确定的,通过-1去掉一个括号再计算。

    3限制:只能走一象限就相当于在op==0上加一个括号序, 保证不走到其它象限。

    #include<bits/stdc++.h>
    using namespace std;
    #define mod 1000000007
    #define ll long long
    #define N 100005
    ll fac[N],inv[N],invfac[N],f[N];
    int read()
    {
        int x=0; int fl=1; char ch=getchar();
        while(ch>'9'||ch<'0') { if(ch=='-') fl=-1; ch=getchar(); }
        while(ch<='9'&&ch>='0') { x=x*10+ch-'0'; ch=getchar(); }
        return x*fl;
    }
    ll quick_pow(ll a,ll k)
    {
        ll ans=1;
        while(k){
            if(k&1) ans=ans*a %mod;
            a=a*a%mod;
            k>>=1;
        }
        return ans;
    }
    ll catlan(int n)
    {
        if(n<0) return 0;
        return fac[2*n]*invfac[n] %mod *invfac[n] %mod * inv[n+1] %mod;
    }
    ll C(int n,int m)
    {
        return fac[n]*invfac[m] %mod *invfac[n-m] %mod ;
    }
    int main()
    {
        freopen("problem.in","r",stdin);
        freopen("problem.out","w",stdout);
        int n,op;
        n=read(); op=read();
        fac[0]=1;
        for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i %mod;
        invfac[n]=quick_pow(fac[n],mod-2);
        for(int i=n;i>=1;i--) invfac[i-1]= invfac[i]*i %mod;
        inv[1]=1;
        for(int i=2; i<=n; ++i)
          inv[i]=((mod-mod/i)*inv[mod%i]%mod+mod)%mod;
        ll ans=0;
        if(op==0){
            //坐标轴内都可以随便走 无论怎么走 横坐标走到的最远的点一定是确定的 且一定要走回来 纵坐标也一样
            //选i步走横坐标 剩下的n-i步走纵坐标 那么i/2步必须走回去 纵坐标也一样 
            for(int i=0;i<=n;i+=2) ans=( ans+ C(n,i)*C(i,i/2) %mod *C(n-i,(n-i)/2)%mod )%mod;
            printf("%lld
    ",ans);
        }
        else if(op==1)//为了不走到负半轴 就必须每个时刻向右走的步数比向左走多 即为括号序 
         printf("%lld
    ",catlan(n/2));
        else if(op==2){
        //定义f[i]为走了i步回到起点的方案数 因为必须走到起点才可以走纵坐标 
        //枚举j表示第一次走回到起点时走过的步数 即之前都没有回到过 所以是catlan(j/2-1)*4 有四个方向
        //-1原因: 
            f[0]=1;//记得初始化 
            for(int i=0;i<=n;i++)
             for(int j=0;j<=i;j+=2)
              f[i]=( f[i] + f[i-j]*catlan(j/2-1)*4 %mod ) %mod ;
            printf("%lld
    ",f[n]);
        }
        else if(op==3){
        //只能走一象限就相当于在op==0上加一个括号序 保证不走到其它象限 
            for(int i=0;i<=n;i+=2) ans=( ans+ C(n,i)*catlan(i/2) %mod *catlan((n-i)/2) %mod )%mod;
            printf("%lld
    ",ans);
        }
        return 0;
    }
    /*
    100 1
    100 2
    */
    View Code
  • 相关阅读:
    03 java中的基本数据类型和运算符
    02 Eclipse安装
    01 HelloWorld
    express不是内部或外部命令
    win10 内存或系统资源不足,无法打开PPT
    win 10中解决“此文件在另外一个进程中运行”的问题
    后台查找密码暴力破解
    DVWA--全等级暴力破解(Burte Force)
    DVWA简单搭建
    破解版
  • 原文地址:https://www.cnblogs.com/mowanying/p/11425759.html
Copyright © 2011-2022 走看看