HDU

来源:互联网 发布:新倩女幽魂辅助软件 编辑:程序博客网 时间:2024/05/17 02:38

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1133

题目大意:m+n个人去买票,m个人有50元,n个人有100元,票价50元,售票处没有50元,问他们全都能买到票的排队方式

解题思路:
这里写图片描述

AC代码:

#include<cstdio>#include<vector>#include<string>#include<algorithm>#include<iostream>#include<cstring>using namespace std;typedef long long LL;const int MAXN = 10000;const int P = (479 << 21) + 1;const int G = 3;const int NUM = 20;struct NTT//用于快速乘法{    LL  _wn[NUM];    LL  _a[MAXN], _b[MAXN], _ans[MAXN];    int _lenth;    char _A[MAXN], _B[MAXN];    LL quick_mod(LL, LL, LL);    NTT(const char *A, const char *B) { strcpy(_A, A);strcpy(_B, B); }    void GetWn();    void Prepare();    void Rader(LL*);    void Ntt(LL*, int);    void Conv();    void Transfer();    void Multiply();};LL NTT::quick_mod(LL x, LL n, LL mod){    LL ans = 1;    x %= mod;    while (n)    {        if (n & 1)            ans = ans * x % mod;        n >>= 1;        x = x * x % mod;    }    return ans;}void NTT::GetWn(){    for (int i = 0; i < NUM; i++)    {        int t = 1 << i;        _wn[i] = quick_mod(G, (P - 1) / t, P);    }}void NTT::Prepare(){    _lenth = 1;    int L1 = strlen(_A);    int L2 = strlen(_B);    while (_lenth <= 2 * L1 || _lenth <= 2 * L2) _lenth <<= 1;    for (int i = 0; i < _lenth; i++)    {        if (i < L1) _a[i] = _A[i] - '0';        else _a[i] = 0;        if (i < L2) _b[i] = _B[i] - '0';        else _b[i] = 0;    }}void NTT::Rader(LL *a){    int j = _lenth >> 1;    for (int i = 1; i < _lenth - 1; i++)    {        if (i < j) swap(a[i], a[j]);        int k = _lenth >> 1;        while (j >= k)        {            j -= k;            k >>= 1;        }        if (j < k) j += k;    }}void NTT::Ntt(LL *a, int on){    Rader(a);    int id = 0;    for (int h = 2; h <= _lenth; h <<= 1)    {        id++;        for (int j = 0; j < _lenth; j += h)        {            LL w = 1;            for (int k = j; k < j + h / 2; k++)            {                LL u = a[k] % P;                LL t = w * a[k + h / 2] % P;                a[k] = (u + t) % P;                a[k + h / 2] = (u - t + P) % P;                w = w * _wn[id] % P;            }        }    }    if (on == -1)    {        for (int i = 1; i < _lenth / 2; i++)            swap(a[i], a[_lenth - i]);        LL inv = quick_mod(_lenth, P - 2, P);        for (int i = 0; i < _lenth; i++)            a[i] = a[i] * inv % P;    }}void NTT::Conv(){    Ntt(_a, 1);    Ntt(_b, 1);    for (int i = 0; i < _lenth; i++)        _ans[i] = _a[i] * _b[i] % P;    Ntt(_ans, -1);}void NTT::Transfer(){    int t = 0;    for (int i = 0; i < _lenth; i++)    {        _ans[i] += t;        if (_ans[i] > 9)        {            t = _ans[i] / 10;            _ans[i] %= 10;        }        else t = 0;    }}void NTT::Multiply(){    GetWn();    Prepare();    Conv();    Transfer();}struct BigNum//针对的都是正数{    char _bignum[MAXN];//倒序保存    int _lenth;    static char* _tag[10];    BigNum() { _lenth = 0; }    BigNum(char*);    BigNum(char*, int);    BigNum(int);    bool operator>(const BigNum&)const;    bool operator<(const BigNum&)const;    bool operator==(const BigNum&)const;    bool operator>=(const BigNum&)const;    BigNum operator=(const BigNum&);    BigNum operator+(const BigNum&)const;    BigNum operator+=(const BigNum&);    BigNum operator-(const BigNum&)const;//大的减小的    BigNum operator-=(const BigNum&);    BigNum operator*(const BigNum&)const;//普通乘法    BigNum operator/(const BigNum&)const;//大的除小的    BigNum multiply(const BigNum&)const;//NTT乘法    friend ostream& operator<<(ostream&, const BigNum&);    friend istream& operator >> (istream&, BigNum&);};char* BigNum::_tag[] = { "0", "1","2","3","4","5","6","7","8","9" };BigNum::BigNum(char *bignum){    _lenth = strlen(bignum);    for (int i = 0;i < _lenth;i++)        _bignum[i] = bignum[_lenth - 1 - i];    _bignum[_lenth] = '\0';}BigNum::BigNum(int smallnum){    _lenth = 0;    while (smallnum)    {        _bignum[_lenth++] = '0' + smallnum % 10;        smallnum /= 10;    }    _bignum[_lenth] = '\0';}BigNum::BigNum(char *bignum, int flag){    strcpy(_bignum, bignum);    _lenth = strlen(bignum);}bool BigNum::operator>(const BigNum& a)const{    if (_lenth > a._lenth) return true;    if (_lenth < a._lenth) return false;    for (int i = _lenth - 1;i >= 0;--i)    {        if (_bignum[i] > a._bignum[i])            return true;        else if (_bignum[i] < a._bignum[i])            return false;    }    return false;}bool BigNum::operator<(const BigNum& a)const{    return a > *this;}bool BigNum::operator==(const BigNum& a)const{    if (a > *this) return false;    if (a < *this) return false;    return true;}bool BigNum::operator>=(const BigNum& a)const{    return *this > a || *this == a;}BigNum BigNum::operator=(const BigNum& a){    strcpy(_bignum, a._bignum);    _lenth = a._lenth;    return *this;}BigNum BigNum::operator+(const BigNum& a)const{    BigNum ans;    int max_len = max(_lenth, a._lenth);    int left = 0;    for (int i = 0;i < max_len;++i)    {        if (i < _lenth)            left += _bignum[i] - '0';        if (i < a._lenth)            left += a._bignum[i] - '0';        ans._bignum[ans._lenth++] = left % 10 + '0';        left /= 10;    }    if (left >= 1)        ans._bignum[ans._lenth++] = left + '0';    ans._bignum[ans._lenth] = '\0';    return ans;}BigNum BigNum::operator+=(const BigNum& a){    return *this = *this + a;}BigNum BigNum::operator-(const BigNum& a)const{    BigNum ans;    int left = 0;    for (int i = 0;i < _lenth;++i)    {        left += _bignum[i] - '0';        if (i < a._lenth)            left -= a._bignum[i] - '0';        if (left >= 0)        {            ans._bignum[ans._lenth++] = left + '0';            left = 0;        }        else        {            ans._bignum[ans._lenth++] = 10 + left + '0';            left = -1;        }    }    while (ans._bignum[ans._lenth - 1] == '0'&&ans._lenth - 1>0)        ans._lenth--;    ans._bignum[ans._lenth] = '\0';    return ans;}BigNum BigNum::operator-=(const BigNum& a){    return *this = *this - a;}BigNum BigNum::operator*(const BigNum& a)const{    BigNum ans;    int tmp[MAXN];    memset(tmp, 0, sizeof(tmp));    for (int i = 0;i < _lenth;i++)        for (int j = 0;j < a._lenth;j++)            tmp[i + j] += (_bignum[i] - '0')*(a._bignum[j] - '0');    int left = 0;    for (int i = 0;i < a._lenth + _lenth - 1;i++)    {        left += tmp[i];        if (left <= 9)        {            ans._bignum[ans._lenth++] = left + '0';            left = 0;        }        else        {            ans._bignum[ans._lenth++] = left % 10 + '0';            left /= 10;        }    }    while (left)    {        ans._bignum[ans._lenth++] = left % 10 + '0';        left /= 10;    }    while (ans._lenth - 1 > 0 && ans._bignum[ans._lenth - 1] == '0')        ans._lenth--;    ans._bignum[ans._lenth] = '\0';    return ans;}BigNum BigNum::operator/(const BigNum& a)const{    BigNum divied = *this, div, ans;    for (int i = _lenth - 1, j = a._lenth - 1;i >= 0;--i, --j, div._lenth++, ans._lenth++)    {        ans._bignum[i] = '0';//答案初始化为0        if (j >= 0)            div._bignum[i] = a._bignum[j];//增加0,注意结构体的数组是倒序存放的        else            div._bignum[i] = '0';    }    div._bignum[div._lenth] = '\0';    ans._bignum[ans._lenth] = '\0';    for (int i = _lenth - 1, k = 0, zero = _lenth - a._lenth;i >= 0;--i, k++)    {        if (k <= _lenth - a._lenth&&k > 0)//如果增加的0都删完了,则不进行删除        {            div = BigNum(div._bignum + 1, 1);//除数不断去掉一个零            zero--;//存储答案的位置,根据除数增加的零的个数判断位置        }        for (int j = 9;j >= (divied._bignum[i] - '0') / (div._bignum[div._lenth - 1] - '0' + 1);j--)        {            if (divied >= BigNum(_tag[j], 1)*div)            {                divied -= BigNum(_tag[j], 1)*div;                ans._bignum[zero] = '0' + j;                break;            }        }        if (zero == 0) break;//防止重复在0位置赋值    }    while (ans._bignum[ans._lenth - 1] == '0'&&ans._lenth - 1>0)        ans._lenth--;//去掉多余的0    ans._bignum[ans._lenth] = '\0';    return ans;}BigNum BigNum::multiply(const BigNum& a)const{    BigNum ans;    NTT tmp(_bignum, a._bignum);    tmp.Multiply();    for (int i = 0;i < tmp._lenth;i++)        ans._bignum[ans._lenth++] = tmp._ans[i] + '0';    while (ans._bignum[ans._lenth - 1] == '0'&&ans._lenth - 1>0) ans._lenth--;    ans._bignum[ans._lenth] = '\0';    return ans;}ostream& operator<<(ostream& out, const BigNum& a){    for (int i = a._lenth - 1;i >= 0;i--)        putchar(a._bignum[i]);    return out;}istream& operator >> (istream& in, BigNum& a){    scanf("%s", a._bignum);    a._lenth = strlen(a._bignum);    reverse(a._bignum, a._bignum + a._lenth);    return in;}const int MAXM = 205;BigNum factorial[MAXM];void toInit(){    factorial[0] = BigNum("1", 1);    for (int i = 1;i < MAXM;++i)        factorial[i] = factorial[i - 1].multiply(BigNum(i));}int main(){    toInit();    for (int n, m, cas = 1;scanf("%d%d", &m, &n) == 2 && (n || m);cas++)    {        printf("Test #%d:\n", cas);        if (m < n)            puts("0");        else            cout << factorial[m + n] * BigNum(m - n + 1) / BigNum(m + 1) << endl;//把组合数化成阶乘    }    return 0;}

令h(0)=1,h(1)=1,catalan数满足递推式:
h(n)= h(0)*h(n-1)+h(1)*h(n-2) + … + h(n-1)*h(0) (n>=2)
递推关系的解为:
h(n)=C(2n,n)/(n+1)=c(2n,n)-c(2n,n-1)(n=0,1,2,…)

附相似的题目:

题目大意:12个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,问排列方式有多少种?

解题思路:把他们从矮到高排列,在第一排的人标号为0,在第二排的人标号为1,题目即转化成对这12个人进行标号,求满足题目的标号方式种类,因为是从矮到高依次标号,在进行标1操作时,必须要满足标0的人数大于等于标1的人数,即转化成买票问题,此时因为是必须要满足由矮到高,所以不需要重排列,答案即为C(2n,n)-C(2n,n-1)

题目大意:矩阵链乘: P=A1×A2×A3×……×An,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,试问有几种括号化的方案?

解题思路:n个矩阵需要连乘(n-1)次,因此需要(n-1)对括号。且这里的括号只是为了使矩阵两两结合,而不是单纯为加括号而加括号,像( (a1) * (a2)),这里将两个矩阵分别括起来是不符合要求的。因此这里如果确定了括号的顺序,那么矩阵的结合顺序也会确定,如(()())对应了(( a1* a2) * (a3 * a4)),这里左括号相当于50元,右括号相当于100元,然后左括号数和右括号数相等。

题目大意:在圆上选择2n个点,将这些点成对连接起来,且所得n条线段不相交,求可行的方法数。

解题思路:将圆上的点依次标为P0,P1,….,P2n-1。设F(2n)为2n个点可行的方案数,选择Pk与P0相连(k属于1到2n-1),同样地可以看出,k必为奇数,否则1至k-1之间有奇数个点,不可能成对连成直线。同样地把k设为2i+1,那么线段P0Pk把剩余的点分为了1…2i和2i+2…2n-1,且新的连线不能与0k相交,它们只能属于0k把圆划分出的这两个区域之一,即F(2n) = ∑F(2i)*F(2n-1-(2i+2)+1) = ∑F(2i)*F(2n-2i-2),(i = 0 … n-1)

题目大意:在一个凸多边形中,通过若干条互不相交的对角线,把这个多边形划分成了若干个三角形。任务是键盘上输入凸多边形的边数n,求不同划分的方案数f(n)

解题思路:因为凸多边形的任意一条边必定属于某一个三角形,所以我们以某一条边为基准,以这条边的两个顶点为起点P1和终点Pn,将该凸多边形的顶点依序标记为P1、P2、……、Pn,再在该凸多边形中找任意一个不属于这两个点的顶点Pk(2<=k<=n-1),来构成一个三角形,用这个三角形把一个凸多边形划分成两个凸多边形,其中一个凸多边形,是由P1,P2,……,Pk构成的凸k边形(顶点数即是边数),另一个凸多边形,是由Pk,Pk+1,……,Pn构成的凸n-k+1边形。此时,我们若把Pk视为确定一点,那么根据乘法原理,f(n)的问题就等价于——凸k多边形的划分方案数乘以凸n-k+1多边形的划分方案数,即选择Pk这个顶点的f(n)=f(k)×f(n-k+1)。而k可以选2到n-1,所以再根据加法原理,将k取不同值的划分方案相加,得到的总方案数为:f(n)=f(2)f(n-2+1)+f(3)f(n-3+1)+……+f(n-1)f(2)。

题目大意:n个结点可构造多少个不同的二叉树。(结点之间没有区别)

解题思路:先取一个点作为顶点,然后左边依次可以取0至n-1个相对应的,右边是n-1到0个,两两配对相乘,就是f(n)=f(0)*fn-1) + f(1)*f(n-2) + …… + f(n-1)*f(0)。

原创粉丝点击