题解:P11256 [GDKOI2023 普及组] 置换
qczrz6v4nhp6u · · 题解
若干年前刚学 OI,看到这个题一点思路都没有,但是现在看来似乎并没有那么困难。
Solution
对于每个置换环考虑。设
解释一下:
通过这个可以得到
对每种长度的置换环分别考虑,最后再将答案乘起来。将
即钦定一个置换环不动,其他置换环乱放的方案数。
考虑其关于
设
直接暴力把这个东西卷起来就是
考虑进一步优化。注意到
暴力算这个东西可以做到
Code
一个
#include<bits/stdc++.h>
using namespace std;
using ui=unsigned int;
using ll=long long;
using ull=unsigned long long;
using i128=__int128;
using u128=__uint128_t;
using pii=pair<int,int>;
#define fi first
#define se second
constexpr int N=3005,mod=998244353;
int n,m,a[N],cnt[N];
bool vis[N];
vector<int> dvs;
ll fac[N],ifac[N];
inline ll qpow(ll a,ll b){
ll res=1;
for(;b;b>>=1,a=a*a%mod)
if(b&1)res=res*a%mod;
return res;
}
ll f[N];
ll dp(int len,int n){
for(int i=0;i<=n;i++)f[i]=0;
f[0]=1;
for(auto &c:dvs){
if(c>n)break;
if(__gcd(m,len*c)==c){
ll g=fac[c-1]*qpow(len,c-1)%mod*ifac[c]%mod;
for(int i=n;i>=0;i--){
ll cur=g;
for(int k=1;c*k<=i;k++,cur=cur*g%mod)
f[i]=(f[i]+f[i-c*k]*cur%mod*ifac[k])%mod;
}
}
}
return f[n]*fac[n]%mod;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
fac[0]=1;
for(int i=1;i<=3000;i++)fac[i]=fac[i-1]*i%mod;
ifac[3000]=qpow(fac[3000],mod-2);
for(int i=3000;i>=1;i--)ifac[i-1]=ifac[i]*i%mod;
int _Test;cin>>_Test;
while(_Test--){
cin>>n>>m;
dvs.clear();
for(int i=1;i<=m;i++)
if(m%i==0)
dvs.emplace_back(i);
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cnt[i]=vis[i]=0;
for(int i=1;i<=n;i++){
int len=0;
for(int x=i;!vis[x];x=a[x])vis[x]=1,++len;
++cnt[len];
}
ll ans=1;
for(int i=1;i<=n;i++)
ans=ans*dp(i,cnt[i])%mod;
cout<<ans<<'\n';
}
return 0;
}