NOIP2013模拟9.29】Mixing Chemicals(组合计数DP)

【NOIP2013模拟9.29】Mixing Chemicals
  • 一道组合计数的题目。

  • 然后我想了两个小时,没想出来,太弱了额额...

  • 首先我们输入的东西连边,然后,我们会得到一颗环套树.

  • 那么我们现在就把问题转化为如何给这棵环套树K染色,使相邻节点颜色不同.

  • 很显然,如果一个节点不在任何一个简单环里,那么他对答案的方案数贡献显然就是\((k-1)\).

  • 而对于环,我们则另开讨论。

  • 那么令\(f[i]\)表示当环的大小为\(i\)时,合法方案数.

  • 那么显然,\(f[1]=k,f[2]=k*(k-1),f[3]=k*(k-1)*(k-2)\).

  • \(i\ge 3\)时,我们就知道,有\[f[i]=f[i-1]*(k-2)+f[i-2]*(k-1)\]

  • 意思就是,讨论一下\(i\)左右两个点的颜色是否相同,如果相同,那么等价于在\(i-2\)大小的环里的方案数*当前有\((k-1)\)种方案,另一种类似。

  • 然后就可以愉快的DP一下,因为n特别小,所以怎么搞环都可以.

#include <iostream>
#include <cstdio>
#include <cstring>

#define F(i, a, b) for (int i = a; i <= b; i ++)
#define mem(a, b) memset(a, b, sizeof a)

const int N = 1e2 + 10, Mo = 1e9 + 7;

int T, n, k, tot, ans, p, total, c[N], f[N], vis[N], fa[N], bz[N];

using namespace std;

int main() {
    for (scanf("%d", &T); T --; ) {
        scanf("%d %d", &n, &k), total = n, mem(vis, 0);
        F(i, 0, n - 1) scanf("%d", &fa[i]);
        
        f[1] = k, f[2] = (1LL * k * (k - 1)) % Mo, f[3] = ((1LL * k * (k - 1) % Mo)* (k - 2)) % Mo, ans = 1;
        F(i, 4, n) f[i] = ((1LL * f[i - 1] * (k - 2) % Mo)+ (1LL * f[i - 2] * (k - 1)) % Mo) % Mo;
        
        F(i, 0, n - 1)
            if (!vis[i]) {
                for (p = i, tot = 0, mem(bz, 0); !bz[p]; bz[p] = 1, p = fa[p], tot ++);
                if (p ^ i) continue;
                for (p = i, mem(bz, 0); !bz[p]; bz[p] = vis[p] = 1, p = fa[p]);
                total -= tot, ans = (1LL * ans * f[tot]) % Mo;
            }

        F(i, 1, total)
            ans = (1LL * ans * (k - 1)) % Mo;

        printf("%d\n", ans);
    }
}

猜你喜欢

转载自www.cnblogs.com/Pro-king/p/9383461.html
今日推荐