【ybt金牌导航8-5-7】魔法手镯(Burnside 引理)(矩阵乘法优化DP)

魔法手镯

题目链接:ybt金牌导航8-5-7

题目大意

给你一个长度为 n 的珠子环,然后规定一些珠子不能相邻。
然后有多少种本质不同的珠子环。
本质相同时指它可以通过旋转变成一样的。

思路

看到循环置换,和颜色,不难想到 Polya 定理。

但是它有限制条件,那我们就考虑用 Burnside 引理。
然后你发现它的限制条件你可以按长度 DP 下去:
fi,j 为一开始的颜色为 i,最后的颜色为 j 的方案数。

然后你发现这个 DP 可以用矩阵乘法优化。

然后你就直接枚举 gcd(i,n) 的结果,每个都求出来结果乘上 ϕ 再最后除 n 就是答案了。

代码

#include<cstdio> #define mo 9973 using namespace std; struct matrix { int n, m; int a[11][11]; }a, one, A; int T, n, m, k, x, y, ans; int jia(int x, int y) { return x + y >= mo ? x + y - mo : x + y; } int mul(int x, int y) { int re = x * y; if (re > mo) re = re - re / mo * mo; return re; } matrix operator *(matrix x, matrix y) {//矩阵乘法 matrix re; re.n = x.n; re.m = y.m; for (int i = 1; i <= re.n; i++) for (int j = 1; j <= re.m; j++) re.a[i][j] = 0; for (int k = 1; k <= x.m; k++) for (int i = 1; i <= re.n; i++) for (int j = 1; j <= re.m; j++) re.a[i][j] = jia(re.a[i][j], mul(x.a[i][k], y.a[k][j])); return re; } matrix jzksm(matrix x, int y) { matrix re = one; while (y) { if (y & 1) re = re * x; x = x * x; y >>= 1; } return re; } int ksm(int x, int y) { int re = 1; x = mul(x, 1); while (y) { if (y & 1) re = mul(re, x); x = mul(x, x); y >>= 1; } return re; } int phi(int n) {//求 phi int re = n; for (int i = 2; i * i <= n; i++) { if (n % i == 0) { re = re / i * (i - 1); while (n % i == 0) n /= i; } } if (n > 1) re = re / n * (n - 1); return mul(re, 1); } int work(int d) { A = jzksm(a, n / d - 1); int re = 0; for (int i = 1; i <= m; i++)//直接上 DP 的结果 for (int j = 1; j <= m; j++) if (a.a[i][j])//记得首尾要连接,所以要首尾能在一起 re = jia(re, A.a[i][j]); ans = jia(ans, mul(re, phi(d)));//直接乘上取这个值的可能性(phi) } int main() { for (int i = 1; i <= 10; i++) one.a[i][i] = 1; scanf("%d", &T); while (T--) { scanf("%d %d %d", &n, &m, &k); one.n = one.m = m; a.n = a.m = m; for (int i = 1; i <= m; i++) for (int j = 1; j <= m; j++) a.a[i][j] = 1; for (int i = 1; i <= k; i++) { scanf("%d %d", &x, &y); a.a[x][y] = a.a[y][x] = 0; } ans = 0; for (int i = 1; i * i <= n; i++) if (n % i == 0) {//枚举 gcd(i,n) 的取值 work(i); if (n / i != i) work(n / i); } ans = ans * ksm(n, mo - 2) % mo; printf("%d\n", ans); } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_8-5-7.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(28)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示