洛谷 P4709 - 信息传递(置换+dp)
一道挺有意思的题罢……
首先看到这种与置换乘法相关的题,首先把这些置换拆成一个个置换环,假设输入的置换有 \(m\) 个置换环,大小分别为 \(s_1,s_2,\cdots,s_m\),显然置换环与置换环的顺序是不影响的,因此我们只用考虑它的大小即可。
其次我们考虑对于一个置换,我们来考虑对其进行 \(k\) 次幂后会得到什么。手玩几组数据就可以发现,原来不在同一个置换环中的元素,进行 \(k\) 次幂后,肯定也不会在同一个置换环中,而有的原本在同一个置换环中的元素进行 \(k\) 次幂后会被拆开,具体来说,对于一个大小为 \(s\) 的置换环,进行 \(k\) 次幂后会拆成 \(\gcd(s,k)\) 个等大小的置换环,道理很明白,对于一个元素,你每次绕着置换环走 \(k\) 步,显然 \(\dfrac{s}{\gcd(s,k)}\) 次就会回到源点,因此 \(k\) 次幂后拆出的置换环中,单个置换环大小就是 \(\dfrac{s}{\gcd(s,k)}\)。
也就是说 \(s_1,s_2,\cdots,s_m\) 中有一些大小相同的置换环能拼起来,显然不同大小的置换环的方案数是独立的,因此我们可以分别求出每个大小的置换环拼起来的方案数再用乘法原理乘起来。考虑设 \(c_i\) 表示大小为 \(i\) 的置换环有多少个,那么根据之前的结论 \(j\) 个大小为 \(i\) 的置换环能够拼起来当且仅当 \(\gcd(ij,k)=j\)。那么怎么求大小为 \(i\) 的置换环拼起来有多少种方案呢?这时候就要用到 \(dp\) 了,我们设 \(dp_j\) 表示用了 \(j\) 个大小为 \(i\) 的置换环的方案数,考虑转移,我们枚举第 \(j\) 个大小为 \(i\) 的置换环跟多少个环在一起拼成大环,假设为 \(r\) 个,那么 \(dp_i=\sum\limits_rdp_{i-r}\dbinom{i-1}{r-1}f(r,i)[\gcd(ir,k)=r]\),其中 \(f(r,i)\) 表示将 \(r\) 个大小为 \(i\) 的置换环拼起来的方案数,注意到 \(\gcd(ir,k)=r\Rightarrow r\mid k\),因此枚举的 \(r\) 必须是 \(k\) 的约数,又 \(\sum c_i=m\) 是 \(\mathcal O(n)\) 级别的,因此这样暴力 DP 总枚举量是 \(n·d(k)\) 的。
最后考虑怎样求 \(f(r,i)\),由于最后拼成的是一个环,因此我们钦定第一个环的第一个元素必须在第一个位置,否则会算重,将剩余 \(r-1\) 个环填入剩余 \(r-1\) 个位置有 \((r-1)!\) 种,剩余 \((r-1)!\) 个环也可自由旋转,方案数为 \(i^{r-1}\),因此 \(f(r,i)=(r-1)!\times i^{r-1}\),随便算算即可。
时间复杂度 \(n·d(k)·\log n\),因为计算 \(f(r,i)\) 时涉及快速幂。
const int MAXN=1e5;
const int MOD=998244353;
int n,p[MAXN+5],c[MAXN+5],dp[MAXN+5],vis[MAXN+5];
int fac[MAXN+5],ifac[MAXN+5];vector<int> f;
int gcd(int x,int y){return (!y)?x:gcd(y,x%y);}
int qpow(int x,int e){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
void init_fac(int n){
for(int i=(fac[0]=ifac[0]=ifac[1]=1)+1;i<=n;i++) ifac[i]=1ll*ifac[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%MOD,ifac[i]=1ll*ifac[i-1]*ifac[i]%MOD;
}
int binom(int n,int k){return 1ll*fac[n]*ifac[k]%MOD*ifac[n-k]%MOD;}
int main(){
scanf("%d",&n);init_fac(n);int ans=1;
for(int i=1;i<=n;i++) scanf("%d",&p[i]);
for(int i=1;i<=n;i++) if(n%i==0) f.pb(i);
for(int i=1;i<=n;i++) if(!vis[i]){
int siz=0;for(int j=i;!vis[j];j=p[j]) vis[j]=1,siz++;
c[siz]++;
}
for(int i=1;i<=n;i++){
dp[0]=1;
for(int j=1;j<=c[i];j++){
dp[j]=0;
for(int k=0;k<f.size()&&f[k]<=j&&i*f[k]<=n;k++)
if(gcd(i*f[k],n)==f[k]){
dp[j]=(dp[j]+1ll*dp[j-f[k]]*binom(j-1,f[k]-1)%MOD*
fac[f[k]-1]%MOD*qpow(i,f[k]-1))%MOD;
}
} ans=1ll*ans*dp[c[i]]%MOD;
} printf("%d\n",ans);
return 0;
}
洛谷 P4709 - 信息传递(置换+dp)的更多相关文章
- 洛谷P2661 信息传递(最小环,并查集)
洛谷P2661 信息传递 最小环求解采用并查集求最小环. 只适用于本题的情况.对于新加可以使得两个子树合并的边,总有其中一点为其中一棵子树的根. 复杂度 \(O(n)\) . #include< ...
- [NOIP2015] 提高组 洛谷P2661 信息传递
题目描述 有n个同学(编号为1到n)正在玩一个信息传递的游戏.在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学. 游戏开始时,每人都只知道自己的生日.之后每一 ...
- 洛谷 P2661 信息传递 Label:并查集||强联通分量
题目描述 有n个同学(编号为1到n)正在玩一个信息传递的游戏.在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学. 游戏开始时,每人都只知道自己的生日.之后每一 ...
- 洛谷P2661 信息传递==coedevs4511 信息传递 NOIP2015 day1 T2
P2661 信息传递 题目描述 有n个同学(编号为1到n)正在玩一个信息传递的游戏.在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学. 游戏开始时,每人都只知 ...
- 洛谷 P2661 信息传递(并查集 & 最小环)
嗯... 题目链接:https://www.luogu.org/problemnew/show/P2661 这道题和一些比较水的并查集不太一样,这道题的思路就是用并查集来求最小环... 首先,如果我们 ...
- NOIP2015提高组T2 洛谷P2661 信息传递
题目描述 有n个同学(编号为1到n)正在玩一个信息传递的游戏.在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学. 游戏开始时,每人都只知道自己的生日.之后每一 ...
- 洛谷——P2661 信息传递
https://www.luogu.org/problem/show?pid=2661#sub 题目描述 有n个同学(编号为1到n)正在玩一个信息传递的游戏.在游戏里每人都有一个固定的信息传递对象,其 ...
- 洛谷 P2661 信息传递 题解
P2661 信息传递 题目描述 有 \(n\) 个同学(编号为 \(1\) 到 \(n\) )正在玩一个信息传递的游戏.在游戏里每人都有一个固定的信息传递对象,其中,编号为 \(i\) 的同学的信息传 ...
- 洛谷p2661信息传递题解
题目 这个题一眼看上去就是用并查集求最小环. 我们可以设两个数组分别是f,d分别表示该点的爸爸和该点到祖先的距离. 当该点的爸爸等于他时,那他肯定就是祖先. 此时信息就肯定传递完了,此时的整个图中(我 ...
随机推荐
- 指标统计:基于流计算 Oceanus(Flink) 实现实时 UVPV 统计
作者:吴云涛,腾讯 CSIG 高级工程师导语 | 最近梳理了一下如何用 Flink 来实现实时的 UV.PV 指标的统计,并和公司内微视部门的同事交流.然后针对该场景做了简化,并发现使用 Flink ...
- JVM:体系结构
JVM:体系结构 本笔记是根据bilibili上 尚硅谷 的课程 Java大厂面试题第二季 而做的笔记 概览 Java GC 主要回收的是 方法区 和 堆 中的内容 类加载器 类加载器是什么 双亲委派 ...
- 2020BUAA软工结伴项目作业
2020BUAA软工结伴项目作业 17373010 杜博玮 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 结伴项目作业 我在这个课程的目标是 学 ...
- 密码学基础:AES加密算法
[原创]密码学基础:AES加密算法-密码应用-看雪论坛-安全社区|安全招聘|bbs.pediy.com 目录 基础部分概述: 第一节:AES算法简介 第二节:AES算法相关数学知识 素域简介 扩展域简 ...
- 寻找下一个结点 牛客网 程序员面试金典 C++ java Python
寻找下一个结点 牛客网 程序员面试金典 C++ java Python 题目描述 请设计一个算法,寻找二叉树中指定结点的下一个结点(即中序遍历的后继). 给定树的根结点指针TreeNode* root ...
- 2021CCPC河南省赛(部分代码待更)
最终A了8道题, 喜提一金, 也是在意料之中. 第一次三个队友集中在一起打比赛, 也体验了一下线下的氛围, 还是比较赞的, 自己也不是说毫无作用, 帮助团队做了几道题, 还是挺满意的. 1002 em ...
- 第11课 OpenGL 飘动的旗帜
飘动的旗帜: 这一课从第六课的代码开始,创建一个飘动的旗帜.我相信在这课结束的时候,你可以掌握纹理映射和混合操作. 大家好!对那些想知道我在这里作了些什么的朋友,您可以先按文章的末尾所列出的链接,下载 ...
- 攻防世界 WEB 高手进阶区 NSCTF web2 Writeup
攻防世界 WEB 高手进阶区 NSCTF web2 Writeup 题目介绍 题目考点 php基本函数语法 加密解密函数 base64_decode().str_rot13() 字符串反转函数 str ...
- Invalid prop: type check failed for prop "xxx". Expected Number, got String.
在子组件progress-circle.vue的template中的定义如下: <svg :width="radius" :height="radius" ...
- uni-app map组件关于marker标记点动态设置的问题
marker是Array类型,赋值的时候只能对整个数组进行更改赋值,不能只改变内部的对象,亲测Vue.$set()也不行 this.marker = [ { latitude: 39.90, long ...