魔法手链

题源:

acw3134.

题意:

\(n\) 个珠子,每个珠子可以是 \(1 \to m\),还是套路的旋转后重合算一种填数方案,比较特殊的是,这题给出 \(k\) 种颜色互斥关系,互斥的颜色不得相邻。

\(\rm Analysis:\)

还是 \(\rm burnside\) 引理,只不过求方案的时候各个环不是互相无关了,需要考虑互相影响。

仍然是套路的发现置换的方案数只和环的个数有关,不妨设为 \(f\),那么先大力化一下式子:

\[\begin{aligned} \sum _ {i = 1} ^ n f((n, i)) &= \sum _ {d \mid n} f(d) \sum _ {i = 1} ^ n [(i, n) = d] \\ &= \sum _ {d \mid n} f(d) \varphi(\frac{n}{d}) \end{aligned} \]

至于 \(f(d)\) 的方案数,这题颜色很少,考虑 \(dp\) 求一波,发现环数为 \(d\) 时,环内不会冲突,只需要考虑环间冲突,而确定了前 \(d\) 个位置,由于每个环内部要求是不动点,则确定了整体,所以只需要考虑前 \(d\)​ 个位置的取值即可,矩阵快速幂优化就行了,另外,需要额外考虑 \(d = 1\) 时,这时候环内有冲突,合法方案数就是不和自己冲突的颜色数。

\(\rm Code:\)

using i64 = long long;
const int md = 9973;

struct Matrix {
    int n, m;
    std::vector<std::vector<int>> a;
    auto& operator[](const int x) {
        return a[x];
    }
    const auto& operator[](const int x) const {
        return a[x];
    }

    Matrix(int n, int m) : n(n), m(m), a(n, std::vector<int>(m, 0)) {}

    friend Matrix operator*(const Matrix &a, const Matrix &b) {
        Matrix c(a.n, b.m);

        for (int k = 0; k < a.m; ++k) for (int i = 0; i < c.n; ++i) for (int j = 0; j < c.m; ++j) 
            c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % md;

        return c;
    }

    inline int pwer(int idx) {
        // std::cerr << idx << '\n';
        // std::cerr << n << ' ' << m << '\n';
        Matrix res(n, m), now = (*this);
        for (int i = 0; i < n; ++i) 
            res[i][i] = 1;

        for (; idx; idx >>= 1, now = now * now) 
            if (idx & 1) res = res * now;

        // std::cerr << "work: 46" << '\n';

        int sum = 0;
        for (int i = 0; i < n; ++i) 
            sum += res[i][i];

        // std::cerr << sum << '\n';

        return sum % md;
    }
};

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int T;
    std::cin >> T;
    while (T--) {
        int n, m, k;
        std::cin >> n >> m >> k;

        Matrix init(m, m);
        // std::cerr << n << ' ' << m << ' ' << k << '\n';

        for (int i = 0; i < m; ++i) for (int j = 0; j < m; ++j) 
            init[i][j] = 1;

        // std::cerr << "work: 89" << '\n';

        for (int i = 0; i < k; ++i) {
            int a, b;
            std::cin >> a >> b;
            --a, --b;
            init[a][b] = 0, init[b][a] = 0;
        }
        // std::cerr << "work: 97" << '\n';

        int res = 0;
        for (i64 i = 1; i * i <= n; ++i) {
            // std::cerr << i << '\n';
            if (n % i != 0) 
                continue;
            res = (res + init.pwer(i) * phi(n / i)) % md;
            if (i * i != n) 
                res = (res + init.pwer(n / i) * phi(i)) % md;

            // std::cerr << i << '\n';
        }

        res = res * pwer(n, md - 2) % md;

        std::cout << res << '\n';
    }
}
posted @ 2021-08-10 17:16  Z_char  阅读(111)  评论(0编辑  收藏  举报