我花了三个小时写了一道题的六千字题解....(POJ 2888 Magic Bracelet)
整理的算法模板合集: ACM模板
点我看算法全家桶系列!!!
实际上是一个全新的精炼模板整合计划
前置知识:小学生都能看懂的群论从入门到升天教程 《群论全家桶》
一道简单的题目
Problem 24.2.1 POJ 2888 Magic Bracelet / AcWing 3134. 魔法手链((Burnside引理,矩阵快速幂优化DP,欧拉函数))
给定 mmm 种不同颜色的魔法珠子,每种颜色的珠子的个数都足够多。
现在要从中挑选 nnn 个珠子,串成一个环形魔法手链。
魔法珠子之间存在 kkk 对排斥关系,互相排斥的两种颜色的珠子不能相邻,否则会发生爆炸。(同一种颜色的珠子之间也可能存在排斥)
请问一共可以制作出多少种不同的手链。
注意,如果两个手链经旋转后能够完全重合在一起,对应位置的珠子颜色完全相同,则视为同一种手链。
答案对 997399739973 取模。
1≤T≤101≤T≤101≤T≤10
1≤n≤1091≤n≤10^91≤n≤109
gcd(n,9973)=1\gcd(n,9973)=1gcd(n,9973)=1
1≤m≤101≤m≤101≤m≤10
0≤k≤m(m+1)20≤k≤\frac{m(m+1)}{2}0≤k≤2m(m+1)
1≤a,b≤m1≤a,b≤m1≤a,b≤m
Solution
这里因为多了很多互斥关系,也就是不同的循环之间有一定有了很多限制,所以不能使用 Polya 定理,只能使用 Burnside引理 。
本题比上一题简化了,只有循环这一种置换。我们可以发现 nnn 很大,需要模 997399739973 ,并且保证 gcd(n,9973)=1\gcd(n,9973)=1gcd(n,9973)=1 也就意味着我们最后除以 nnn 的时候可以直接使用乘法逆元。
我们设旋转的距离为 k=0,1,2,⋯n−1k=0,1,2,\cdots n-1k=0,1,2,⋯n−1。
由于需要使用 Burnside引理,所以我们需要求一下不动点的数量。
设一个点初始位置为 xxx ,那么每转一次就会转到 x+kx+kx+k 的位置
如图 24.2.1 所示
那么转多少会重复呢(回到起点,出现 循环)
回到起点不一定只赚一圈,可能需要转很多圈,由于点数是有限的,所以我们这样每次走 kkk 步是一定会重复的。
这里很明显可以得到一个同余方程:
x+kt≡x(modn)x+kt\equiv x(\mod n)x+kt≡x(modn)
kt≡0(modn)kt\equiv 0(\mod n)kt≡0(modn)
即
kt=nr+ckt=nr+ckt=nr+c
t=ndt=\cfrac{n}{d}t=dn
其中 d=gcd(n,k)d =\gcd(n,k)d=gcd(n,k)
这也就意味着我们走 nd\cfrac{n}{d}dn 步就会出现 循环
即:每一个循环一共有 nd\cfrac{n}{d}dn 个点。
即:每个循环均使用了 nd\cfrac{n}{d}dn 个点,而我们一共有 nnn 个点,
所以一共会有 nnd=d\cfrac{n}{\frac{n}{d}}=ddnn=d 个循环。
即我们仅有旋转这一种置换操作,会得到 d=gcd(n,k)d=\gcd(n,k)d=gcd(n,k) 个循环。
我们发现 每个循环均有 nd\cfrac{n}{d}dn 个点,我们可以先分析一个循环,其余的循环与该循环性质相同,分析一个循环即可得到所有的循环的答案。
我们发现了这一个循环的 nd\cfrac{n}{d}dn 个点的一个性质:我们按照原本的点的编号的顺序看,任意两个相邻的点之间的距离均为 ddd 。
证明: d=gcd(n,k)d=\gcd(n,k)d=gcd(n,k)
设 k′=kdk'=\cfrac{k}{d}k′=dk,n′=ndn'=\cfrac{n}{d}n′=dn。
由于我们每次走都是跳 kkk 步,然后如果跳过了一圈长度为 nnn 以后,就 %n\% n%n。因为 kkk 和 nnn 是 ddd 的倍数,所以我们从起点 xxx 出发,每次走到的点到起点的距离都必然是 ddd 的倍数。然后因为我们一共走了 nd\cfrac{n}{d}dn 步,而每一步又都是 ddd 的倍数,也就是说我们每次走的就是距离可以写成表格,并且我们将 0,d,2d⋯0,d,2d\cdots0,d,2d⋯ 做一个映射:
距离 0 d 2d 3d ... (n / d - 1) * d = n - 1
步数 0 1 2 3 ... n / d
映射 0 1 2 3 ... n / d
也就意味着我们每一步走的距离均为 ddd 。
综上所诉,该性质得证 □
这个性质也就意味着对于每一个循环(我们当前分析的就是一个循环),我们只需要考虑 ddd 的倍数的这 nd\cfrac{n}{d}dn 个点即可。
所以我们仅考虑这些点的映射(0,1,2,3,⋯0,1,2,3,\cdots0,1,2,3,⋯),就会得到一个长度为 n′=ndn'=\cfrac{n}{d}n′=dn 的一个小环(共有 n′=ndn'=\cfrac{n}{d}n′=dn 个点),并且每次跳的距离是 k′k'k′,其中 k′=kdk'=\cfrac{k}{d}k′=dk,即 k′k'k′ 与 nnn 互质。也就可以证明这样跳,一定能遍历到这个小环的所有的点。
小环如图 24.2.2 所示
因为可以遍历这个小环的所有的点,可以得到:
k′×0,k′×1,k′×2,⋯,k′×(n−1)k'\times 0,k'\times 1,k'\times 2,\cdots,k'\times (n-1)k′×0,k′×1,k′×2,⋯,k′×(n−1) 构成了一个 000 ~ nnn 的一个简化剩余系。( 即该 k′k'k′ 的序列在 modn\mod nmodn 意义下还是 000 ~ n−1n-1n−1)
证明:
我们使用反证法。
假设k′×0,k′×1,k′×2,⋯,k′×(n−1)k'\times 0,k'\times 1,k'\times 2,\cdots,k'\times (n-1)k′×0,k′×1,k′×2,⋯,k′×(n−1) 不是000 ~ nnn 简化剩余系,即序列中存在两个下标, i≠ji≠ji=j 且 k′i=k′jk'i=k'jk′i=k′j
k′i=k′jk'i=k'jk′i=k′j
实际上就是:
k′(i−j)≡0(modn)k'(i-j)\equiv0(\mod n)k′(i−j)≡0(modn)
因为 k′k'k′ 与 nnn 互质,所以可以吧 k′k'k′ 去掉
即:i≡j(modn)i\equiv j(\mod n)i≡j(modn)
而 0≤i,j<n0\le i,j< n0≤i,j<n。
若 i≡j(modn)i\equiv j(\mod n)i≡j(modn),则定有 i=ji=ji=j,与前提不符,产生矛盾。
故对于所有的 k′ik'ik′i,k′jk'jk′j ,0≤i,j<n0\le i,j <n0≤i,j<n,任意两个数均不相同,故在 %n\%n%n 的意义下一定能遍历完 000 ~ n−1n-1n−1 里的每一个数,故一定是 000 ~ n−1n-1n−1 的一个简化剩余系。
综上所诉,该性质得证 □
由于我们刚刚得到的这一个循环的 nd\cfrac{n}{d}dn 个点的一个性质:我们按照原本的点的编号的顺序看,任意两个相邻的点之间的距离均为 ddd 。
以及另一个性质 k′×0,k′×1,k′×2,⋯,k′×(n−1)k'\times 0,k'\times 1,k'\times 2,\cdots,k'\times (n-1)k′×0,k′×1,k′×2,⋯,k′×(n−1) 构成了一个 000 ~ nnn 的一个简化剩余系,也就意味着在 %n\%n%n 的意义下一定能遍历完 000 ~ n−1n-1n−1 里的每一个数
而我们仅考虑这些点的映射(0,1,2,3,⋯0,1,2,3,\cdots0,1,2,3,⋯),就会得到一个长度为 n′=ndn'=\cfrac{n}{d}n′=dn 的一个小环(共有 n′=ndn'=\cfrac{n}{d}n′=dn 个点),并且每次跳的距离是 k′k'k′。我们再回代,即乘上一个 ddd 以后,就意味着可以遍历所有与 xxx 的距离为 ddd 的倍数的点。
而最开始我们由得到了仅考虑旋转这一种置换,我们一共会有 ddd 个循环,每次循环的步数(走的距离)均为 ddd ,也就是每个循环的起点,为 x,x+d,x+2d,⋯x,x+d,x+2d,\cdotsx,x+d,x+2d,⋯。
也就意味着第一个循环的区间为 [x,x+d−1][x,x+d-1][x,x+d−1],第二个循环的区间为 [x+d,x+2d−1][x+d,x+2d-1][x+d,x+2d−1],以此类推。我们之前说过,由于仅是旋转操作,得到的 ddd 个循环的解法一摸一样,每一个循环的不动点的个数均相同,即为每一个循环内部的点的颜色按照顺序相同。
例如:第一个循环的颜色为 1 2 3 4 5
,则第二个循环的颜色同样为 1 2 3 4 5
,以此类推。
具体图形如图 24.2.3 所示:
因为每一个循环区间内部点的颜色都是相同的,也就意味着每个区间的最后一个点和下一个区间的第一个点相邻,而下一个区间的第一个点和这个区间的第一个点的颜色是相同的,所以我们就可以看作每个区间的最后一个点和该区间的第一个点是相邻的,也就意味着每个长度为 ddd 的小区间又可以看作是一个循环,也就可以画成一个更小的圈。
也就意味着我们只需要讨论一下这个长度为 ddd 的圈有多少个染色方案就行啦!
这个染色方案的数量就是这个置换的这个循环的不动点的数量。(因为这个圈固定以后,这一个段就固定了,那么这一小段的这个区间固定了以后,因为整个环分为 ddd 段,也就是 ddd 个循环,那么整个环也就固定了)
也就是说我们只需要求一下长度为 ddd 的,满足要求的这个环的染色方案即可。
那么怎么求解这个方案数呢?我们仅需枚举一下这个环的起点
我们分情况讨论一下,我们枚举一下 $d$这个点是那种颜色,因为一共有 $m$ 种颜色,所以我们 $1$ ~ $m$ 枚举一遍。
我们求一下 ddd 染乘 111 的所有方案, ddd 染 222 的所有方案,⋯\cdots⋯ 到 ddd 染 mmm 的所有方案。
若 ddd 染的颜色为 iii ,我们可以使用 DP 来求解这个答案。
f[i][j]f[i][j]f[i][j] 表示的是染完了前 iii 个珠子的颜色,并且最后一个珠子的颜色为 jjj 的所有方案的数量。
例如我们将 ddd 染为 iii,即 f[0][i] = 1
,其余均为 000:f[0][1] = f[0][2] = ...f[0][m] = 0;
。(ddd 就是 111 前面的这个珠子,编号为 000)
暴力转移即可:
f[i][j] += vis[k][j] ? f[i - 1][k] : 0;
其中 vis[k][j]
表示 kkk 和 jjj 是否互斥(由题目输入),如果不互斥的话说明 kkk 和 jjj 可以相邻。
最后的答案:分类讨论:
若 ddd 染色为 111 ,答案为 f[d][1]
。其余同理。
但是由于本题的 n≤109n\le 10^9n≤109 ,暴力转移无法通过,所以我们可以使用矩阵乘法来优化。
设 F[i]=f[i][1...m]F[i]=f[i][1...m]F[i]=f[i][1...m]
则 F[i]=F[i−1]∗MF[i] = F[i - 1] * MF[i]=F[i−1]∗M
其中转移矩阵 M 就代表 若kkk 和 jjj 可以相邻,互不排斥,就为 111 ,否则就为 000 。(该矩阵的转移方程实际上展开以后就是上面的DP转移方程)
则答案 F[d]=F[0]∗MdF[d]=F[0]*M^dF[d]=F[0]∗Md,我们可以来使用快速幂优化。
总时间复杂度为 O(M3logn)O(M^3logn)O(M3logn)
最后一个问题,因为 nnn 很大,所以我们枚举 kkk 的时候不可能直接枚举。
我们发现 kkk 的唯一作用就是得到 d=gcd(n,k)d=\gcd(n,k)d=gcd(n,k),而我们非常容易就可以发现很多 gcd(n,k)gcd(n,k)gcd(n,k) 是相同的,因此我们就可以将 kkk 按照所有的最大公约数分类,也就是直接枚举gcd(n,k)\gcd(n,k)gcd(n,k) 即可,也即是说我们只需要枚举 nnn 的约数即可,那么就意味着最多只会有 φ(n≤109)≤1600\varphi(n\le10^9)\le 1600φ(n≤109)≤1600 种。
则对于所有的 d=gcd(n,k)d=\gcd(n,k)d=gcd(n,k) 都可以直接使用快速幂来求解。分类之后我们肯定要算一下每一个 ddd 都包含了多少个 kkk,换句话说就是一共有多少个 kkk 满足 gcd(n,k)=d\gcd(n,k)=dgcd(n,k)=d 呢?
我们发现这就是一个 非常经典的欧拉函数问题。
gcd(n,k)=d\gcd(n,k)=dgcd(n,k)=d
即:gcd(nd,kd)=1\gcd(\frac{n}{d},\frac{k}{d})=1gcd(dn,dk)=1 的个数,也就是在000 ~ nd\cfrac{n}{d}dn 种互质的个数,也就是 φ(nd)\varphi(\cfrac{n}{d})φ(dn)。直接暴力求,复杂度 O(φ(nd)O(\sqrt{\varphi(\cfrac{n}{d}})O(φ(dn)。
这里的答案(所有不动点的和)就是 ans=∑d=0nF[0]∗Md∗φ(nd)ans=\sum_{d=0}^{n}F[0]*M^d*\varphi(\cfrac{n}{d})ans=d=0∑nF[0]∗Md∗φ(dn)
最后的答案就是所有不动点的平均值即不动点个数和除以所有置换的个数。
因为我们这里的置换是旋转,一共有 nnn 个点,所以一共有 nnn 种置换
即最后的答案为:ansn\cfrac{ans}{n}nans,也就是乘上 nnn 模 997399739973 的逆元 ans×inv(n)ans\times inv(n)ans×inv(n)
Code
#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;const int N = 11, P = 9973;int m;
struct Matrix
{int a[N][N];Matrix(){memset(a, 0, sizeof a);}
};Matrix operator* (Matrix a, Matrix b)
{Matrix c;for (int i = 1; i <= m; i ++ )for (int j = 1; j <= m; j ++ )for (int k = 1; k <= m; k ++ )c.a[i][j] = (c.a[i][j] + a.a[i][k] * b.a[k][j]) % P;return c;
}int qmi(Matrix a, int b)
{Matrix res;for (int i = 1; i <= m; i ++ ) res.a[i][i] = 1;while (b){if (b & 1) res = res * a;a = a * a;b >>= 1;}int sum = 0;for (int i = 1; i <= m; i ++ ) sum += res.a[i][i];return sum % P;
}int phi(int n)
{int res = n;for (int i = 2; i * i <= n; i ++ )if (n % i == 0){res = res / i * (i - 1);while (n % i == 0) n /= i;}if (n > 1) res = res / n * (n - 1);return res % P;
}int inv(int n)
{n %= P;for (int i = 1; i < P; i ++ )if (i * n % P == 1)return i;return -1;
}int main()
{int T;cin >> T;while (T -- ){int n, k;cin >> n >> m >> k;Matrix tr;for (int i = 1; i <= m; i ++ )for (int j = 1; j <= m; j ++ )tr.a[i][j] = 1;while (k -- ){int x, y;cin >> x >> y;tr.a[x][y] = tr.a[y][x] = 0;}int res = 0;for (int i = 1; i * i <= n; i ++ )if (n % i == 0){res = (res + qmi(tr, i) * phi(n / i)) % P;if (i != n / i)res = (res + qmi(tr, n / i) * phi(i)) % P;}cout << res * inv(n) % P << endl;}return 0;
}
讲完啦!六千字的题解,快写死我了,你看懂了嘛?(●ˇ∀ˇ●)
看不懂没关系,点个赞就懂了 = ̄ω ̄=
我花了三个小时写了一道题的六千字题解....(POJ 2888 Magic Bracelet)相关推荐
- 明日之后茅斯沼泽宝箱位置及开箱技巧:花了三个小时整理的最全宝箱位置
明日之后宝箱位置都在哪里?明日之后茅斯沼泽宝箱怎么开?玩明日之后的都知道,在各个地图上有各种宝箱,打开宝箱可以开出不同的物件,像枪械涂层配件.铁铸件螺丝.配方残页等.有的宝箱可以直接打开有的则需要万能 ...
- 三万六千字通关MySQL面试
本文作者:ThinkWon,感谢提供这么详细的资源. 数据库基础知识 为什么要使用数据库 数据保存在内存 优点:存取速度快 缺点:数据不能永久保存 数据保存在文件 优点:数据永久保存 缺点:1)速度比 ...
- 大学毕业论文字数有上限吗,我写了一万六千字,指导老师让我删掉一万字关键第二天就要交,怎么办?...
毕业论文的字数是有学校规定限制的,一般本科的字数为8000-12000字, 也有一些学校的字数要求是2万以上,不同学校的要求不同,建议你下载下 本校的毕业写作规范,上面会有明细的要求,包括写多少字,查 ...
- “应付”大学作业,我花3小时写了一个“文本转手写”神器
作者 | Saurabh Daware 译者 | 弯月,责编 | 郭芮 来源 | CSDN(ID:CSDNnews) 最近,有一个名叫Saurabh Daware的印度大学生只花了3个小时就编写了一款 ...
- 【源码共享】我花2小时写了微信官网的响应式布局HTML+CSS 换成旅行主题风格更炫酷了
微信官网仿写效果 ↑ 移动端响应式效果 ↑ 微信官网首页,简约干净,能学习写好这个首页,就能掌 握HTML网页设计前端盒子的布局.嵌套,及css效果的 使用... 微信官网首页主要有以下几个需要关注的 ...
- java三角形判断器_花了两个小时做了那么一个很丑的Java写的三角形判断器.........
闲着无事,花了两三个小时做了那么一个自己都看不下去的Java小程序,可用来判断三角形的类型,输入三边,可以求出各个角的正弦.余弦以及正切值! 说实话,丑得连自己都看不下去了! (不喜勿喷! ) 话不多 ...
- 为什么我花了三个半月准备的面试还是砸了?附 iOS 开发者求职攻略
最近我被一家公司拒了. 为了申请这家公司我花了三个半月的心血.我事先研究了这个公司的一切,对它了如指掌.他们的创始人在网上公开发表过的任何东西我都可以倒背如流. 不过我还是想的太美了. 就像在博客上写 ...
- ASUS R556L华硕老笔记升级,换固态硬盘,鸟枪换炮记:买固态硬盘的纠结和艰辛的系统迁移(前后花了三天时间)
某天看到网络一篇文章:老笔记本提速 机械硬盘换固态硬盘 [华硕r556l](https://www.jianshu.com/p/1abf7f32c0ed),最近固态硬盘也比较便宜.250G,200RM ...
- 蜡笔小新里的钢达姆机器人怎么画_写字机器人好用吗? 组装就花了5个小时 还要学习软件、录入字体...
据江苏公共·新闻频道<新闻360>报道:新学期开学,各种各样的课后作业成了孩子和家长关注的话题.前不久,一条"孩子购买代写作业机器人,被家长发现"的新闻,引发了强烈争议 ...
最新文章
- 性能提升3倍的树莓派4,被爆设计缺陷!
- Spring security获取当前用户
- nginx假死导致的问题回顾
- 陕西省天然气行业十四五建设展望及发展战略规划报告2021版
- 云原生时代,.NET5必将称王!
- 统一对比学习框架?没错它来了。
- SQL Server 2016中的本机JSON支持
- Hibernate之session的管理方式
- Java学习笔记(05)
- 关于recycle.exe病毒的查杀
- uploadify php 重命名,Uploadify_THINKPHP配置说明
- J2ME基本术语词典(05/06/09)
- html添加好友界面,添加好友.html
- Ubuntu18.04安装有道词典
- cordic ip核 vivado_Xilinx Vivado Cordic 核的使用
- ActivityScenario启动失败Activity never becomes requested state [RESUMED, STARTED, CREATED, DESTROYED]
- redis 安装以及redis desktop manger 连接
- RASP | 远程Java应用的RASP调试教程
- 400分理科学计算机,400分左右的理科大学 高考400分能上什么学校
- Angular-CLI工具使用文档翻译