特殊计数数列——例题

一.Catalan数

1.Train Problem II

题意:给你一个序列,有N个元素(N<=100),这个序列中的元素是1,2,3,4…N。现在从1到n将数字放入到一个栈里面,可以一边放一边出栈。问有多少种出栈的方式?
思路: 稍微想一下就能发现,这其实就是Catalan数的版题…将每一次入栈看做是+1,将每一次出栈看做是-1,那么整个操作就映射成为了一个只包含+1,-1的长度为2n的一个序列。因为栈中要么有元素,要么没有元素,那么也就是说我们映射出来的这个序列的前缀和是始终>=0的(前缀和的含义此时就是栈中的元素个数)。那么就是Catalan数了,即 Cn=1n+1C(2n,n) C n = 1 n + 1 C ( 2 n , n ) 。(注意要用高精度)
代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 100
#define MAXL 1000
using namespace std;
typedef int hp[MAXL+5];
hp C[MAXN+5],d;
void Clear(hp &x)
{
    for(int i=x[0];i>=0;i--)
        x[i]=0;
}
void Mul(hp a,int b,hp &c)
{
    Clear(d);
    int len=a[0];
    for(int i=1;i<=len;i++)
    {
        d[i]+=a[i]*b;
        d[i+1]+=d[i]/10;
        d[i]%=10;
    }
    len++;
    while(d[len]!=0)
    {
        d[len+1]+=d[len]/10;
        d[len]%=10;
        len++;
    }
    while(len>1&&d[len]==0)
        len--;
    d[0]=len;
    for(int i=0;i<=len;i++)
        c[i]=d[i];
}
void Div(hp a,int b,hp &c)
{
    Clear(d);
    int len=a[0],res=0;
    for(int i=len;i>=1;i--)
    {
        res=10*res+a[i];
        d[i]+=res/b;
        res%=b;
    }
    while(len>1&&d[len]==0)
        len--;
    d[0]=len;
    for(int i=0;i<=len;i++)
        c[i]=d[i];
}
void Print(hp x)
{
    for(int i=x[0];i>=1;i--)
        printf("%d",x[i]);
    printf("\n");
}
void prepare()
{
    C[1][0]=1,C[1][1]=1;
    for(int i=2;i<=MAXN;i++)
    {
        Mul(C[i-1],4*i-2,C[i]);
        Div(C[i],i+1,C[i]);
    }
}
int main()
{
    int n;
    prepare();
    while(scanf("%d",&n)==1)
        Print(C[n]);
    return 0;
}
2.Pills

题意: 有一些药片,有一个人。这个人每次吃药只吃一个药的一半。首先他会拿出一颗完整的药,分成两半,吃一半,再把另一半放回去。接下来,他会每次取出一颗或半颗药片。如果是完整的,就分成两半,做和第一片颗药片同样的事情;否则就吃下那半颗药。设取出完整的药片为W,取出半颗药片为H,那么就会形成一个序列。问能形成多少种不同的序列。(N颗药,N<=1000)
思路: 相当于是把每颗药分成两半,一半是W,一半是H。但是只有取出了W后才能够取出H。像之前那道题一样,就可以把W看成+1,把H看成-1,那么就又是前缀和始终>=0的问题了,和之前那道题是一样的了,就不赘述了。(同样还是要高精度呢…)

3.How Many Trees?

题意: 我们都知道二叉树搜索树的定义吧?现在有一个为1,2,3,…,n的一个序列,问根据这个序列构建多少种二叉搜索树(因为用二叉搜索树表示一个序列的方法并不唯一)。
思路: 可以这样来想:枚举i为根,那么1~i-1必然为i的左子树,i+1~n必然为右子树,而左右子树互不干扰,就可以把两者的方案数相乘。那么就有:

Ansn=Ans0Ansn1+Ans1Ansn1+...+Ansn2Ans1+Ansn1Ans0 A n s n = A n s 0 A n s n − 1 + A n s 1 A n s n − 1 + . . . + A n s n − 2 A n s 1 + A n s n − 1 A n s 0

Ansn=Cn ⇒ A n s n = C n

顺便提一句,现在才发现其实加括号问题也可以转变为+1-1的模型呢(’(‘为+1,’)’为-1,一共有n-1个+1和n-1个-1,然后就是sum大于等于0了)。然后继续高精度…也不附加代码了,就是前面的那一个。

4.Game of Connections

题意: 给你2*n个数,按顺序围成一个环,要求在两两之间连线,并且连线之间被没有交点,并且使得每一个点有且仅有另一个点与它相连。问连线的方案数。
思路: 模仿上一道题,我么可以先连一条边,然后设gn为点的个数为2*n的时候的答案。
显然有的是,现在连了一条边之后,分成的两块中,每块仍然含有偶数个点,否则将会无解。那么就将问题分成了两个子问题。假设枚举的是1连得是谁,那么就可以让左边剩余0,1,2,…n-1对,右边就是相应的补集。然后数学推导和上一道题是一样的。

好像这几道题都是经典模型的说,代码都是一模一样的…(〃’▽’〃)

5.有趣的数列

题意: 一个1到2n的排列,被称作一个有趣的数列,当且仅当:
1. 所有的奇数项满足a[1] < a[3] < … < a[2n-1],所有的偶数项满足a[2] < a[4] < … < a[2n];
2. 任意相邻的两项a[2i-1]与a[2i](1 ≤ i ≤ n)满足奇数项小于偶数项,即:a[2i-1] < a[2i]。
思路: 这道题就没有之前的那么好想了。
这样来想:从1扫到2n,然后选择当前这个数是奇数还是偶数。并且保证每一个位置的已经选择的奇数的数量一定大于偶数的数量。这是可以证明的。假设当前选的奇数的数量等于偶数的数量,如果这个时候再选一个偶数,那么后面选的一个奇数无论怎样选,都比当前的偶数大,就错误了。所以说这样子是对的。这样子就是+1-1的模型了,那么就是 Ans2n=Cn A n s 2 n = C n

好像Catalan数再怎么找还是那几个模型,这样子就够了吧.
1. +1-1模型
2. 乘法序列加括号
3. 凸多边形三角划分
4. 构造二叉树的方案数

二.斐波那契数列

似乎相对来说斐波那契的题要少一些呢,因为大多数都是出现在综合类的题目里面,这里不好作为专题提出来。

1.problem B

题意:度熊面前有一个全是由1构成的字符串,被称为全1序列。你可以合并任意相邻的两个1,从而形成一个新的序列。对于给定的一个全1序列,请计算根据以上方法,可以构成多少种不同的序列。(直接贴过来的,比较好理解)n<=100
思路:
考虑末尾的1。这个1可以和它相邻的那个1合并,就变成了ans[n-2];也可以选择不合并,就变成了ans[n-1]。那么显然就有F[n]=F[n-1]+F[n-2]。

2.[Poi1997]Jump

补坑啦φ(>ω<*)
题意: 有一个数轴,每个整点有一个格子。格子上面放了一些棋子,且一个格子可以放多颗棋子。现在有一些移动方案(设当前位置为P):
(1).让P-1和P-2的位置的棋子数量-1,让P的位置的棋子数量+1;
(2).让P的位置的棋子数量-1,让P+1,P+2的位置的棋子数量+1;
现在要你求一个终止局面,使得一个格子里面最多有1颗棋子。
思路:
借鉴于:[构造 || 斐波那契分解] BZOJ 2912 [Poi1997]Jump
题目看完之后感觉是很像斐波那契数列,因为都是涉及到P-2,P-1,P的问题,且系数都是一样的,但是还是做不起…
其实可以这样来想:将所有位置的棋子数量乘上那一位所对应的斐波那契数,也就是: tot=ni=1(numi×Fi) t o t = ∑ i = 1 n ( n u m i × F i ) ,然后又因为我们知道任意一个正整数都可以分解成若干个不相邻的斐波那契数的加和,所以说我们可以将tot进行分解,这样就实现了题目的要求了。但是这样是不是成立的呢?从样例看来是对的。
但是还没有完。因为将正整数分解成为若干个不相邻的斐波那契数的加和的结论需要这个正整数在斐波那契数列中的位置尽量靠后,也就是尽量大。那么我们就需要将棋盘进行平移来达到这个目的。但是在实际实现中又不可能无限大,于是需要定义一个平移的常量。链接中给的是80,并且也有具体的证明,可以去膜拜参考一下。
代码在这里(高进度需要压位):

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define MAXN 11000
#define MAXL 1000
#define B 80
#define P 100000000LL
using namespace std;
typedef long long LL;
typedef LL hp[MAXL+5];
vector<int> ans;
hp F[MAXN+5];
hp tot,d,totn;
int a[MAXN+5],b[MAXN+5];
void Clear(hp &x)
{
    for(int i=x[0];i>=0;i--)
        x[i]=0;
}
void Add(hp a,hp b,hp &c)
{
    Clear(d);
    int len=max(a[0],b[0]);
    for(int i=1;i<=len;i++)
    {
        d[i]+=a[i]+b[i];
        d[i+1]+=d[i]/P;
        d[i]%=P;
    }
    len++;
    while(len>1&&d[len]==0)
        len--;
    d[0]=len;
    for(int i=0;i<=len;i++)
        c[i]=d[i];
}
void Dec(hp a,hp b,hp &c)
{
    Clear(d);
    int len=max(a[0],b[0]);
    for(int i=1;i<=len;i++)
    {
        if(a[i]<b[i])
        {
            a[i+1]--;
            a[i]+=P;
        }
        d[i]+=a[i]-b[i];
    }
    len++;
    while(len>1&&d[len]==0)
        len--;
    d[0]=len;
    for(int i=0;i<=len;i++)
        c[i]=d[i];
}
void Mul(hp a,LL b,hp &c)
{
    Clear(d);
    int len=a[0];
    for(int i=1;i<=len;i++)
    {
        d[i]+=a[i]*b;
        d[i+1]+=d[i]/P;
        d[i]%=P;
    }
    len++;
    while(d[len]!=0)
    {
        d[len+1]+=d[len]/P;
        d[len]%=P;
        len++;
    }
    while(len>1&&d[len]==0)
        len--;
    d[0]=len;
    for(int i=0;i<=len;i++)
        c[i]=d[i];
}
int Cmp(hp a,hp b)
{
    if(a[0]>b[0])
        return 1;
    if(a[0]<b[0])
        return -1;
    for(int i=a[0];i>=1;i--)
        if(a[i]>b[i])
            return 1;
        else if(a[i]<b[i])
            return -1;
    return 0;
}
int main()
{
    int n,len=0,maxval=-P;
    scanf("%d",&n);
    tot[0]=1,tot[1]=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d %d",&a[i],&b[i]);
        maxval=max(maxval,a[i]);
    }
    F[0][0]=1,F[0][1]=1;
    F[1][0]=1,F[1][1]=1;
    for(len=2;len<=maxval+B;len++)
        Add(F[len-2],F[len-1],F[len]);
    len--;
    for(int i=1;i<=n;i++)
    {
        Clear(totn);
        Mul(F[a[i]+B],1LL*b[i],totn);
        Add(tot,totn,tot);
    }
    while(Cmp(F[len],tot)<0)
        len++,Add(F[len-2],F[len-1],F[len]);
    int pos=len;
    while(tot[0]>1||tot[1]!=0)
    {
        if(Cmp(F[pos],tot)<=0)
        {
            Dec(tot,F[pos],tot);
            ans.push_back(pos-B);
        }
        pos--;
        if(pos<0)
            break;
    }
    sort(ans.begin(),ans.end());
    for(int i=0;i<(int)ans.size();i++)
        printf("%d ",ans[i]);
    return 0;
}

三.格路径与Schroder数

搜遍了百度似乎也只找到这一道题:B君的多边形
思路:
其实就是之前的Part2末尾所说的凸多边形切割(不一定非要是三角切割的那种),求方案数。
那么根据之前的结论,这道题的答案就是小Schroder数的第n项了(有n+1条边)。
又有s[n]=R[n]/2,那么就可以通过算R[n]来计算s[n]。而计算R[n]的式子是O(n)的,恰好这道题也支持,就这样解决了。

现在能够找到的大概就是这些了。如果说之后还有遇到的题目,将会以单道题的题解的形式发放出来的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值