HDU - 1133
2017-08-19 23:10
120 查看
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1133
题目大意:m+n个人去买票,m个人有50元,n个人有100元,票价50元,售票处没有50元,问他们全都能买到票的排队方式
解题思路:
AC代码:
令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)。
题目大意: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)。
相关文章推荐
- hdu 1133
- hdu 1133 Buy the Ticket
- HDU 1133 - Buy the Ticket 【动态规划+组合 ~递推 偶遇 卡特兰】
- hdu 1133
- HDU 1133 Buy the Ticket
- hdu 1133 Buy the Ticket
- HDU 1133 Buy the Ticket【卡特兰数】
- hdu 1133
- hdu 1133 Buy the Ticket(递推+精度精算)
- HDU 1133
- hdu 1133
- HDU-1133-Buy the Ticket
- hdu 1133
- hdu-1133-Buy the Ticket
- hdu 1133(卡特兰数+大数乘除+阶乘)
- HDU-1133 Buy the Ticket (Catalan数)
- HDU 1133 (数论 or DP、高精度;Java版)
- hdu 1133 卡特兰数的应用 java水大数
- hdu 1133 Buy the Ticket
- hdu 1133