置换 perm.cpp
程序员文章站
2022-04-01 15:36:59
...
【一句话题意】定义置换现在有一个长度为n的置换p。求大于0的最小的k使得[1…n]pk=[1…n]。k对19184192取模。n<=1e5
【分析】刚开始没有看懂题意,在打了一个暴力模拟置换的程序之后才大致明白。无外乎是重复按照p的方式进行置换,问最小k次之后序列会回到初始状态。开始的问题在于我是以普通四则运算的方式来看待这个新定义的运算,很明显在这个式子中交换律和结合律是不满足的,习惯性的依赖过去的经验容易引起题面理解上的歧义。
理解了题面之后一切就变得很简单了。在p当中一定会有多个不等长的置换环,求他们的lcm就是这道题的答案。
然而求lcm的算法其实不止一种,选择合适的算法,不仅能通过更强的数据,而且往往能节省下大量编写和调试时间。
考场上有相当一部分的人选择了最为直观的算法,利用lcm的传递性再套用高精度解决一切,甚至是高精除低精之后的gcd。导致出现了认为今天是高精专场,产生了错误的预期,失去继续打比赛的信心。
这里介绍另一种算法,lcm其实也是所有数的质因数最高次相乘,这样只要对每个数分解质因数,每次更新每个质数的最高次数。最后快速幂相乘一下,过程中就可以直接取模避免高精度,同时也可以适应更强的数据(比如n<=1e7,当然n较小可能也是考场上不能想到更好算法的一大原因)。
【code】
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int maxn=1e5+1000;
const int mod=19184192;
int n,tot;
int b[maxn],p[maxn],used[maxn];
inline void read(int &x){
x=0;char tmp=getchar();
while(tmp<'0'||tmp>'9') tmp=getchar();
while(tmp>='0'&&tmp<='9') x=(x<<1)+(x<<3)+tmp-'0',tmp=getchar();
}
int fac[maxn],prime[maxn],cnt;
int num[maxn];
void Eular(){
fac[1]=1;
for(int i=1;i<=1e5;i++){
if(!fac[i]) fac[i]=i,prime[++cnt]=i;
for(int j=1;j<=cnt&&prime[j]*i<=1e5;j++){
fac[prime[j]*i]=prime[j];
if(prime[j]==fac[i]) break;
}
}
}
int pw(int x,int p){
int ret=1;
while(p>0){
if(p&1) ret=(ret*x)%mod;
x=(x*x)%mod,p>>=1;
}
return ret;
}
signed main(){
freopen("perm.in","r",stdin);
freopen("perm.out","w",stdout);
Eular();
cin>>n;
for(int i=1;i<=n;i++)read(b[i]);//problem
for(int i=1;i<=n;i++){
if(!used[i]){
int tmp=0,pos=i;
while(!used[pos]){//mark used
used[pos]=1;
pos=b[pos];
tmp++;
}
p[++tot]=tmp;//save each number
}
}
for(int i=1;i<=tot;i++){
int x=p[i],d,e;
while(x>1){
d=fac[x],e=0;
while(x%d==0) x/=d,e++;
num[d]=max(num[d],e);//save each largest prime
}
}
int ans=1;
for(int i=1;i<=cnt;i++)
if(num[prime[i]]>0) ans=(ans*pw(prime[i],num[prime[i]]))%mod;//mul all prime to get the lcm(you can mod the number when you do it)
cout<<ans<<endl;
return 0;
}
上一篇: STM32F103 定时器中断实验