HZOI 树屋阶梯

题目描述

原题来自:AHOI 2012

暑假期间,小龙报名了一个模拟野外生存作战训练班来锻炼体魄,训练的第一个晚上,教官就给他们出了个难题。由于地上露营湿气重,必须选择在高处的树屋露营。小龙分配的树屋建立在一颗高度为 N+1N+1N+1 尺的大树上,正当他发愁怎么爬上去的时候,发现旁边堆满了一些空心四方钢材(如图 1.1),经过观察和测量,这些钢材截面的宽和高大小不一,但都是 111 尺的整数倍,教官命令队员们每人选取 NNN 个空心钢材来搭建一个总高度为 NNN 尺的阶梯来进入树屋,该阶梯每一步台阶的高度为 111 尺,宽度也为 111 尺。如果这些钢材有各种尺寸,且每种尺寸数量充足,那么小龙可以有多少种搭建方法?

image

注:为了避免夜里踏空,钢材空心的一面绝对不可以向上。

输入格式

一个正整数 NNN,表示阶梯的高度。

输出格式

一个正整数,表示搭建方法的个数。

注:搭建方法个数可能很大。

样例

样例输入 3

样例输出 5

样例说明

555 种搭建方法如下图:

image

数据范围与提示

对于全部数据,1≤N≤500。


背景

  这是今天下午的t3。对我来说最简单的一道,也是唯一一道两遍AC的题目。

  简直丢人,我居然一开始没看出来是高精度。。。QAQ

  然后我就只写了个普通版的,发现WA掉之后才回来改的。


思路分析

  其实这题还是不好看出来的。

  首先递推关系是肯定能看出来的。

  同机房某神犇教我怎么看,TA说:

  看啊,样例输入3,输出5,这不就是卡特兰数嘛?多显然。

  ??????我*****的。

  没看出来!

  当然如果看出来就最好了【怎么可能???

  没看出来也没问题,交给我吧。

  开始分析:

    首先,有图的题目一定要认真的观察图。

    然后我们来仔细将这张图分解一下

     

    这是一个4层的阶梯,我们把它分解一下。

这有很多种的构成方式,我们先尝试一下第一种。

  第一种:

    

    然后观察一下右边红色的部分,把它先拆掉。

    方案一:

    

    方案二:

    

    方案..............

    在这种构成方法下,我们能构成可行的树屋阶梯为 1 × 5 = 5 个。

  第二种:

      

    方案一:

      

    方案二:

      

    等等等等。。。

    这种构成方法下,我们能构成可行的树屋阶梯 1 × 2 = 2 个。

  第n种:

    。。。。。。

  本质上是和上面的 2 种构成方法是一样的。所有方案数加在一起,得 f4 = 14

结论:

  我们发现:对于任何大小的树屋阶梯,都可以由左上角放一块大小为 j 的以及右下角放一块大小为 i - j - 1 的树屋阶梯,再在空缺的地方由单个大块的矩形填充即可构成;

  这个构成的树屋阶梯一共有 (j) + (i - j - 1) + 1个钢材,正好是 个。

  因为 j 可以在 0 ~i - 1 取且可以证明每一个构成的树屋阶梯一定各不相同,所以我们可以得到树屋阶梯方案与大小关系的递推式

  同时,我们规定 f0f1 = 1。

  来了来了,这不就是卡特兰数的递推式吗?


细节问题

  ?????一定要注意数据范围。

  这题需要高精,原因是什么就不用说了吧。

  那么这里介绍一种玄学,额似乎也不玄学的优化。

  

  跑的贼快。

  Python? 滚!

  高精? 滚!


代码奉上

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

const int MA=120001;
int n;
int fz[MA],fm[MA];
int ans[MA*100],cnt;//ans最大整数的位数 

int fpow(int x,int b) {
    int res=1;
    while(b) {
        if(b&1) 
            res*=x;
        b>>=1;
        x*=x;
    }
    return res;
}

void mul(int x) {
    int k=0;//进位 
    for(int i=1;i<=cnt;i++) {
        ans[i]*=x;
        ans[i]+=k;
        k=ans[i]/10;
        ans[i]%=10;
    }
    while(k) {
        ans[++cnt]+=k;
        k=ans[cnt]/10;
        ans[cnt]%=10;
    }
    return;
}

int main()
{
    scanf("%d",&n);
    for(int i=2;i<=n;i++) {
        int a=n+i;
        for(int j=2;j*j<=a;j++) {//从1开始没有意义
            while(!(a%j)) {
                fz[j]++;
                a/=j;
            }
        }
        if(a>1) 
            fz[a]++;
        int b=i;
        for(int j=2;j*j<=b;j++) {//从1开始没有意义 
            while(!(b%j)) {
                fm[j]++;
                b/=j;
            }
        }
        if(b>1) 
            fm[b]++;
    }
    cnt=1;
    ans[0]=1;
    ans[1]=1;//卡特兰数
    for(int i=2;i<=2*n;i++) {
        if(!fz[i]) 
            continue;
        fz[i]-=fm[i];
        if(!fz[i])
            continue;
        int x=fpow(i,fz[i]);
        if(x!=1) 
            mul(x);
    } 
    for(int i=cnt;i>=1;i--) 
        cout<<ans[i];
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/qxyzili--24/p/11222470.html
今日推荐