HRC 003 T3 置换
HRC 是啥
HZOI Regular Contest
前置知识
解法
就像对于一个数,我们经常从素因子之积的角度看待它一样,在这道题中,我们从轮换的角度看待置换。
我们考虑一个轮换变成恒等变换所需次数:一个长度为
而对于整个置换,若变成恒等置换,则贡献为每个轮换长度的最小公倍数。答案即为每一种最小公倍数的平方与置换总数,即
我们考虑统计出每一种最小公倍数的出现次数,设
先来考虑局部转移:将
同时看到,在整体的转移过程中,得到
部分分代码
#include<bits/stdc++.h>
using namespace std;
unordered_map<long long,long long> dp[300];
unordered_map<long long,bool> vis[300];
long long n,p,t[300],tmaxn[300],inv[100100],invt[300];
long long gcd(long long a,long long b){
long long tem=0;
for(;a;){
tem=a;
a=b%a;
b=tem;
}
return b;
}
long long lcm(long long a,long long b){
return a*b/gcd(a,b);
}
long long qpow(long long x,long long y){
if(!y) return 1;
long long tem=qpow(x,y>>1);
if(y&1) return x*tem%p*tem%p;
else return tem*tem%p;
}
long long finv(long long x){
if(x<min(p,1ll*100100)) return inv[x];
else return qpow(x,p-2);
}
void dfs(long long x,long long y){
if(!x){
if(y>=100100) printf("%lld\n",y);
return;
}
for(int i=1;i<=x;++i){
dfs(x-i,lcm(y,i));
}
}
int main(){
freopen("perm.in","r",stdin);
freopen("perm.out","w",stdout);
scanf("%lld%lld",&n,&p);
inv[0]=1;
inv[1]=1;
for(int i=2;i<min(p,1ll*100100);++i){
inv[i]=(p-p/i)*inv[p%i]%p;
}
t[0]=1;
for(int i=1;i<=n;++i){
t[i]=t[i-1]*i%p;
}
invt[n]=qpow(t[n],p-2);
for(int i=n-1;i;--i){
invt[i]=invt[i+1]*(i+1)%p;
}
invt[0]=1;
long long ans=0;
dp[0][1]=1;
vis[0][1]=1;
for(int s=1;s<=n;++s){
for(int i=n;i>=0;--i){
for(int k=(n-i)/s;k;--k){
for(auto j:vis[i]){
if(!dp[i][j.first]) continue;
vis[i+k*s][lcm(j.first,s)]=1;
long long tem=dp[i][j.first]*finv(qpow(s,k))%p*invt[k]%p;
dp[i+k*s][lcm(j.first,s)]=(dp[i+k*s][lcm(j.first,s)]+tem)%p;
}
}
}
}
for(auto i:vis[n]){
ans=(ans+dp[n][i.first]*(i.first)%p*(i.first)%p)%p;
}
printf("%lld",ans);
return 0;
}
正解
我们发现由于
正解
#include<bits/stdc++.h>
using namespace std;
const long long pr[6]={2,3,5,7,11,13};
struct node{
int pos,lft;
friend bool operator < (node x,node y){
return x.lft<y.lft;
}
}data[300];
long long n,p,base[3000],bcnt,t[300],dp[300][9000],up[300][9000],maxn[300],inv[100100],invt[300];
long long gcd(long long a,long long b){
long long tem=0;
for(;a;){
tem=a;
a=b%a;
b=tem;
}
return b;
}
inline long long lcm(long long a,long long b){
return a*b/gcd(a,b);
}
long long qpow(long long x,long long y){
if(!y)
return 1;
long long tem=qpow(x,y>>1);
if(y&1) return x*tem%p*tem%p;
else return tem*tem%p;
}
void dfs(int now,long long tim,long long sum){
if(now==6){
++bcnt;
base[bcnt]=tim;
return;
}
dfs(now+1,tim,sum);
long long tem=1;
for(;;){
tem*=pr[now];
if(sum+tem>n)
break;
dfs(now+1,tim*tem,sum+tem);
}
}
int main(){
freopen("perm.in","r",stdin);
freopen("perm.out","w",stdout);
scanf("%lld%lld",&n,&p);
dfs(0,1,0);
sort(base+1,base+bcnt+1);
for(int i=1;i<=n;++i){
data[i].pos=i;
data[i].lft=i;
for(int j=0;j<6;++j){
for(;!(data[i].lft%pr[j]);){
data[i].lft/=pr[j];
}
}
}
sort(data+1,data+n+1);
inv[0]=1;
inv[1]=1;
for(int i=2;i<min(p,1ll*100100);++i){
inv[i]=(p-p/i)*inv[p%i]%p;
}
long long ans=0;
dp[0][1]=1;
for(int lft=1,now=1;lft<=n;lft=now){
for(int i=0;i<=n;++i){
for(int j=1;j<=bcnt;++j){
up[i][j]=dp[i][j];
}
}
long long timr=data[lft].lft*data[lft].lft%p;
for(;data[lft].lft==data[now].lft;++now){
int s=data[now].pos;
for(int j=bcnt;j;--j){
int temlcm=lower_bound(base+1,base+bcnt+1,lcm(base[j],data[now].pos/data[now].lft))-base;
if(temlcm>bcnt) continue;
for(int i=n-s;i>=0;--i){
if(!up[i][j]) continue;
int temi=i+s;
long long div=inv[s];
for(int k=1;temi<=n;++k,temi+=s,div=div*inv[s]%p*inv[k]%p){
up[temi][temlcm]=(up[temi][temlcm]+up[i][j]*div)%p;
}
}
}
}
for(int i=0;i<=n;++i){
for(int j=1;j<=bcnt;++j){
dp[i][j]=(dp[i][j]+timr*(up[i][j]-dp[i][j]))%p;
}
}
}
for(int i=1;i<=bcnt;++i){
ans=(ans+dp[n][i]*base[i]%p*base[i])%p;
}
ans=(ans+p)%p;
printf("%lld",ans);
return 0;
}
本文作者:Pursuing_OIer
本文链接:https://www.cnblogs.com/blog21012004/p/18464317
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步