卡特兰数
卡特兰数列是这样的一个数列:
H 1 H_1 H1 | H 2 H_2 H2 | H 3 H_3 H3 | H 4 H_4 H4 | H 5 H_5 H5 | H 6 H_6 H6 | H 7 H_7 H7 | H 8 H_8 H8 |
---|---|---|---|---|---|---|---|
1 | 1 | 2 | 5 | 14 | 42 | 132 | 429 |
通项公式:
- H n = ( 2 n n ) − ( 2 n n − 1 ) H_n=\Large{2n \choose n}-{2n \choose n-1} Hn=(n2n)−(n−12n)
- H n = ( 2 n n ) n + 1 H_n=\large\dfrac{2n \choose n}{n+1} Hn=n+1(n2n)
递推关系:
- H n = { ∑ i = 1 n H i − 1 H n − i n ≥ 2 , n ∈ N + 1 n = 0 , 1 H_n = \begin{cases} \displaystyle\sum_{i=1}^n H_{i-1}H_{n-i} &\text{} n\ge 2,n∈N_+ \\ 1 &\text{} n=0,1 \end{cases} Hn=⎩⎪⎨⎪⎧i=1∑nHi−1Hn−i1n≥2,n∈N+n=0,1
- H n = H n − 1 ( 4 n − 2 ) n + 1 H_n=\Large\frac{H_{n-1}(4n-2)}{n+1} Hn=n+1Hn−1(4n−2)
可以用来计算不穿过对角线的非降路径条数之类的问题,如P1044 栈。
生成函数
用一个级数来表示某种序列
需要数学推导和多项式乘法
目前还未找到好用的FFT模板,这个NTT模板也不是很好用
#include <bits/stdc++.h>
using namespace std;
int m;
const long long mod = 40961, G = 3, Ginv = (mod + 1) / 3;
int r[240100];
long long ksm(long long a, long long b, long long mod)
{
long long ans = 1;
for (; b; b >>= 1, a = a * a % mod)
if (b & 1)
ans = ans * a % mod;
return ans;
}
void NTT(int limit, long long *l, int opt)
{
for (int i = 0; i < limit; ++i)
if (i < r[i])
swap(l[i], l[r[i]]);
for (int mid = 1; mid < limit; mid <<= 1)
{
long long wn, len = mid << 1;
if (opt == 1)
wn = ksm(G, (mod - 1) / len, mod);
else
wn = ksm(Ginv, (mod - 1) / len, mod);
for (int j = 0; j < limit; j += len)
{
long long w = 1;
for (int k = j; k < mid + j; ++k, (w *= wn) %= mod)
{
int x = l[k], y = w * l[k + mid] % mod;
l[k] = (x + y) % mod;
l[k + mid] = (x - y + mod) % mod;
}
}
}
if (opt == -1)
{
long long inv = ksm(limit, mod - 2, mod);
for (int i = 0; i <= limit; i++)
l[i] = l[i] * inv % mod;
}
}
struct BigInt
{
int len;
bool Is_Pos;
long long v[5000];
BigInt()
{
len = 0;
memset(v, 0, sizeof(v));
Is_Pos = 1;
}
BigInt(int x)
{
if (x >= 0)
Is_Pos = 1;
else
x = -x, Is_Pos = 0;
len = 0;
memset(v, 0, sizeof(v));
while (x)
{
v[++len] = x % 10;
x /= 10;
}
}
friend bool operator<(const BigInt &a, const BigInt &b)
{
if (a.len < b.len)
return 1;
if (a.len > b.len)
return 0;
for (int i = a.len; i >= 1; --i)
{
if (a.v[i] < b.v[i])
return 1;
if (a.v[i] > b.v[i])
return 0;
}
return 0;
}
};
BigInt n, a, b;
ostream &operator<<(ostream &out, const BigInt &a);
istream &operator>>(istream &in, BigInt &a);
BigInt operator-(BigInt a, BigInt b);
BigInt operator+(BigInt a, BigInt b);
BigInt operator*(BigInt a, BigInt b);
BigInt operator+(BigInt a, BigInt b)
{
if (!a.Is_Pos && !b.Is_Pos)
{
a.Is_Pos = b.Is_Pos = 1;
BigInt c = a + b;
c.Is_Pos = 0;
return c;
}
if (!a.Is_Pos && b.Is_Pos)
{
a.Is_Pos = b.Is_Pos = 1;
return b - a;
}
if (a.Is_Pos && !b.Is_Pos)
{
a.Is_Pos = b.Is_Pos = 1;
return a - b;
}
int len = a.len + b.len;
BigInt c;
c.len = len;
for (int i = 1; i <= len; ++i)
c.v[i] = a.v[i] + b.v[i];
for (int i = 1; i <= len; ++i)
{
if (c.v[i] >= 10)
{
++c.v[i + 1];
c.v[i] -= 10;
}
}
while (c.len && !c.v[c.len])
c.len--;
return c;
}
BigInt operator-(BigInt a, BigInt b)
{
if (!a.Is_Pos && !b.Is_Pos)
{
a.Is_Pos = b.Is_Pos = 1;
return b - a;
}
if (!a.Is_Pos && b.Is_Pos)
{
a.Is_Pos = 1;
BigInt c = a + b;
c.Is_Pos = 0;
return c;
}
if (a.Is_Pos && !b.Is_Pos)
{
b.Is_Pos = 1;
BigInt c = a + b;
return c;
}
if (a.Is_Pos && b.Is_Pos && a < b)
{
BigInt c = b - a;
c.Is_Pos = 0;
return c;
}
int len = max(a.len, b.len);
BigInt c;
for (int i = 1; i <= len; ++i)
c.v[i] = a.v[i] - b.v[i];
c.len = len;
for (int i = 1; i <= c.len; ++i)
{
if (c.v[i] < 0)
{
c.v[i + 1]--;
c.v[i] += 10;
}
}
while (c.len && !c.v[c.len])
c.len--;
return c;
}
BigInt operator*(BigInt a, BigInt b)
{
int limit = 1, tot, l = 0;
BigInt c;
a.len--;
b.len--;
for (int i = 0; i <= a.len; ++i)
a.v[i] = a.v[i + 1];
a.v[a.len + 1] = 0;
for (int i = 0; i <= b.len; ++i)
b.v[i] = b.v[i + 1];
b.v[b.len + 1] = 0;
while (limit <= a.len + b.len)
limit <<= 1, l++;
for (int i = 0; i <= limit; i++)
r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
NTT(limit, a.v, 1);
NTT(limit, b.v, 1);
for (int i = 0; i <= limit; i++)
a.v[i] = a.v[i] * b.v[i] % mod;
NTT(limit, a.v, -1);
for (int i = 0; i <= limit; i++)
c.v[i] = a.v[i];
for (int i = 0; i <= limit; i++)
{
if (c.v[i] >= 10)
{
c.v[i + 1] += c.v[i] / 10, c.v[i] %= 10;
if (i + 1 > limit)
limit++;
}
}
for (int i = limit; i >= 0; i--)
if (c.v[i] == 0)
limit--;
else
break;
c.len = limit + 1;
for (int i = c.len; i >= 1; --i)
c.v[i] = c.v[i - 1];
c.v[0] = 0;
for (int i = 1; i <= c.len; ++i)
swap(c.v[i], c.v[c.len - i + 1]);
if (a.Is_Pos != b.Is_Pos)
c.Is_Pos = 0;
else
c.Is_Pos = 1;
return c;
}
BigInt operator/(BigInt a, long long b)
{
BigInt c;
int d = 0;
for (int i = a.len; i >= 1; --i)
c.v[++c.len] = ((d * 10 + a.v[i]) / b), d = (d * 10 + a.v[i]) % b;
for (int i = 1; i <= c.len / 2; ++i)
swap(c.v[i], c.v[c.len - i + 1]);
if (!a.len || !b || (a.Is_Pos && b > 0) || (!a.Is_Pos && b < 0))
c.Is_Pos = 1;
else
c.Is_Pos = 0;
while (c.v[c.len] == 0 && c.len > 1)
--c.len;
return c;
}
BigInt operator%(BigInt a, long long b)
{
BigInt c = a / b;
return a - c * b;
}
bool operator>(const BigInt &a, const BigInt &b)
{
return b < a;
}
bool operator!=(const BigInt &a, const BigInt &b)
{
if (a > b || a < b)
return true;
else
return false;
}
bool operator==(const BigInt &a, const BigInt &b)
{
if (a != b)
return false;
else
return true;
}
bool operator<=(const BigInt &a, const BigInt &b)
{
if (a > b)
return false;
else
return true;
}
bool operator>=(const BigInt &a, const BigInt &b)
{
if (a < b)
return false;
else
return true;
}
istream &operator>>(istream &in, BigInt &a) //cin
{
char lin[5010];
int len;
scanf("%s", lin + 1);
len = a.len = strlen(lin + 1);
if (lin[1] == '-')
a.Is_Pos = 0, a.len--;
else
a.Is_Pos = 1;
for (int i = 1; i <= a.len; ++i)
a.v[i] = lin[len - i + 1] - '0';
return in;
}
ostream &operator<<(ostream &out, const BigInt &a) //cout
{
if (!a.len)
{
cout << "0";
return out;
}
if (!a.Is_Pos)
cout << "-";
for (int i = a.len; i >= 1; i--)
printf("%d", a.v[i]);
return out;
}
int main()
{
cin >> a >> b;
cout << a * b;
return 0;
}