您的位置:首页 > 其它

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代码:

#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)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: