Address
Solution
- 考虑怎样构造一种方案。
- 对于周围的
n
个点组成的环,我们将其拆分成任意条链,并在每一条链上选择任意一个点向中心点连接一条边,这样显然就是一种合法方案。
- 于是我们就可以用 DP 来计算方案数了。
- 设
f[i]
表示拆分到第
i
个点为止的方案数,则
f[i]=∑j=1if[i−j]×j
。
- 即表示每次新确定一条长度为
j
的链,链上的
j
个点都可以向中心点连边。
- 但这样有一些问题,我们会发现 DP 中的第 1 个点可以不是固定的。
- 也就是说,我们构造出的方案可以通过旋转得到一些不同的方案,而 DP 中却不会考虑。
- 因此,当我们确定第 1 条链(
j=i
,从
f[0]
转移过来)时,我们可以把这条链进行旋转,在原来的基础上还会得到
i
种不同的方案,总共
i2
种方案。
- 为什么是
i
种而不是
n
种?
- 因为这时第 1 个点只能在这条链上移动,如果超出了这个位置,就相当于在确定其它的链,会重复计算方案数。
- 因此完整的转移方程为
f[i]=i2+∑j=1i−1f[i−j]×j
。
- 注意需要使用高精。
Code
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int Mod = 1e7;
const int N = 105;
inline void CkMax(int &x, int y) {if (x < y) x = y;}
struct Bignum
{
int g[10], len;
inline void Init(int k)
{
memset(g, 0, sizeof(g));
g[len = 1] = k;
}
friend inline Bignum operator * (Bignum x, int k)
{
for (int i = 1; i <= x.len; ++i) x.g[i] *= k;
for (int i = 1; i <= x.len; ++i)
x.g[i + 1] += x.g[i] / Mod, x.g[i] %= Mod;
while (x.g[x.len + 1] > 0) ++x.len;
return x;
}
inline Bignum operator += (const Bignum &x)
{
CkMax(len, x.len);
for (int i = 1; i <= len; ++i)
{
g[i] += x.g[i];
g[i + 1] += g[i] / Mod;
g[i] %= Mod;
}
while (g[len + 1] > 0) ++len;
}
inline void Print()
{
printf("%d", g[len]);
for (int i = len - 1; i; --i)
printf("%07d", g[i]);
}
}f[N];
int main()
{
int n; scanf("%d", &n);
f[0].Init(1);
for (int i = 1; i <= n; ++i)
{
f[i].Init(i * i);
for (int j = 1; j < i; ++j)
f[i] += f[i - j] * j;
}
f[n].Print();
}