P6276 [USACO20OPEN] Exercise P 题解
forest114514 · · 题解
一个更无脑的(?)GF做法(至少所有的步骤都是常见操作),还有这是第一次见有 DS 优化的纯计数题还挺有趣的。
所有排列的置换的所有环长 LCM 之积,我们直接拆贡献到每个质数,相当于就是对于一个
计算至少存在一个考虑容斥,我们先钦定一个子集合法,相当于设计容斥系数
有标号计数考虑 EGF 分配点的编号,注意到我们环的 EGF 形式是
所以对于
注意到乘上
注意到
因为
所以我们的答案为
注意到
注意到
偷懒写的线段树,反正本题
const int N=7600;
int n,M,P,fac[N];
LL ksm(LL a,LL b,LL mod){
LL res=1;for(;b;b>>=1,a=a*a%mod) if(b&1) res=res*a%mod;return res;
}
struct DS{
int mul[N<<2];
void init(int x,int l,int r){
if(l==r) return mul[x]=l,void();
int mid=l+r>>1;
init(ls(x),l,mid),init(rs(x),mid+1,r);
mul[x]=1ll*mul[ls(x)]*mul[rs(x)]%P;
}
int query(int x,int l,int r,int L,int R){
if(L<=l&&r<=R) return mul[x];
int mid=l+r>>1,res=1;
if(mid>=L) res=query(ls(x),l,mid,L,R);
if(mid<R) res=1ll*res*query(rs(x),mid+1,r,L,R)%P;
return res;
}
}TR;
bitset<N> vis;
LL pre[N],suf[N];
LL solve(int x){
LL res=0;
int lim=n/x;
pre[0]=1;
rep(i,1,lim) pre[i]=(1ll*x*(i-1)+P-1)%P*pre[i-1]%P;
suf[lim+1]=1;
per(i,lim,1) suf[i]=suf[i+1]*x%P*i%P;
LL tmp=1,cur=0;
for(cur=x;cur<=n;cur+=x)tmp=tmp*TR.query(1,1,n,cur-x+1,cur-1)%P;
cur-=x;
if(cur<n) tmp=tmp*TR.query(1,1,n,cur+1,n)%P;
rep(i,1,lim) res+=tmp*suf[i+1]%P*pre[i]%P;
return (P-res%P)%P;
}
LL ret[N];
bool _ED;
signed main(){
fprintf(stderr,"%.20lf MB\n",(&_ST-&_ED)/1048576.0);
read(n,M),P=M-1;
TR.init(1,1,n);
fac[0]=1;
rep(i,1,n) fac[i]=fac[i-1]*i%P;
LL ans=1;
rep(i,2,n){
if(vis[i]) continue;
for(int j=i+i;j<=n;j+=i) vis[j]=1;
int k=1;
for(int j=i;;j*=i,++k){
ret[k]=solve(j);
if(j>n/i) break;
}
ret[k+1]=0;
rep(j,1,k) ans=ans*ksm(i,(ret[j]-ret[j+1]+P)*j%P,M)%M;
}
write(ans,'\n');
fprintf(stderr,"%.4lf s\n",1.0*clock()/CLOCKS_PER_SEC);
return 0;
}