魔法手链
题源:
题意:
\(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';
}
}
我不想就这样沦陷,迷失在黑夜,我将燃烧这生命,就算再壮烈。