BZOJ1002 [FJOI2007]轮状病毒

Address


Solution

  • 考虑怎样构造一种方案。
  • 对于周围的 n 个点组成的环,我们将其拆分成任意条链,并在每一条链上选择任意一个点向中心点连接一条边,这样显然就是一种合法方案。
  • 于是我们就可以用 DP 来计算方案数了。
  • f [ i ] 表示拆分到第 i 个点为止的方案数,则 f [ i ] = j = 1 i f [ i j ] × j
  • 即表示每次新确定一条长度为 j 的链,链上的 j 个点都可以向中心点连边。
  • 但这样有一些问题,我们会发现 DP 中的第 1 个点可以不是固定的。
  • 也就是说,我们构造出的方案可以通过旋转得到一些不同的方案,而 DP 中却不会考虑。
  • 因此,当我们确定第 1 条链( j = i ,从 f [ 0 ] 转移过来)时,我们可以把这条链进行旋转,在原来的基础上还会得到 i 种不同的方案,总共 i 2 种方案。
  • 为什么是 i 种而不是 n 种?
  • 因为这时第 1 个点只能在这条链上移动,如果超出了这个位置,就相当于在确定其它的链,会重复计算方案数。
  • 因此完整的转移方程为 f [ i ] = i 2 + j = 1 i 1 f [ 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();
}
发布了104 篇原创文章 · 获赞 125 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/bzjr_Log_x/article/details/79343027