題目描述:
給定一個到的的排列,問有多少個不同的有標號無根樹,滿足如果與有邊那麼與之間也有邊,對取模。
題目分析:
首先將排列拆成一個個的置換環。
考慮兩個循環之間的連邊,由題,如果向連邊,那麼在環上的下一個點也要向在環上的下一個點連邊。
稍加思考可以發現:
- 長度爲的環向長度爲的環連邊,(假設),那麼一定滿足,否則就會連出環。(考慮的第個點和第個點,它們都向的第1個點和第點連邊)
- 兩個環之間只能連最多1條邊,方案數爲。
- 長度大於3的環內不能連邊(如果環長爲奇數會成環,如果環長爲偶數當與更小的環連邊時會連出環)
- 要形成一棵樹,必須至少有1個長度的環。(長度爲2的環可以環內連邊形成一個連通塊,長度爲1的環可以將所有連通塊連通。)
- 樹的生成方式一定是形如最小的環爲根,然後按照大小依次將環掛在樹上。(大環同時接兩個小環會連出環)
(PS:上面所說的連出環是指連的邊形成了環)
於是我們依次考慮每種大小的循環:
假設現在有個長度爲的循環,那麼一定是這個循環先連成個連通分量,然後每個連通分量裏有一個循環向更小的循環連邊。
枚舉:
- 個連通分量向更小的循環連邊的方案數是,其中表示在長度爲的循環裏面的點的個數。
- 每個連通分量內部連邊,一條邊有種連法,總共條邊,方案數爲。
- 最後的問題就是把個點連成個連通分量的方案數,這可以看做加了一個虛點,強制它的度數爲(這樣它的兒子就是k個連通分量),用prufer序列計算,在個位置中出現次,其餘位置隨便填,方案數爲
- 所以總的方案數就是
- 令,則上式可以化爲
如果有長度爲1的循環肯定2要向1連,如果沒有的話就要選一個2的循環內部連邊使圖形成一棵樹。
Code:
#include<bits/stdc++.h>
#define maxn 500005
using namespace std;
const int mod = 998244353;
int T,n,p[maxn],cnt[maxn],f[maxn];
bool vis[maxn];
inline int Pow(int a,int b){
int s=1; for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) s=1ll*s*a%mod;
return s;
}
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&p[i]),cnt[i]=f[i]=vis[i]=0;
for(int i=1;i<=n;i++) if(!vis[i]){
int len=1;
for(int j=p[i];j!=i;j=p[j]) vis[j]=1,len++;
cnt[len]++;
}
for(int i=1;i<=n;i++)
for(int j=i+i;j<=n;j+=i)
f[j]+=cnt[i]*i;
int ans=0;
if(cnt[1]){
ans=cnt[1]>1?Pow(cnt[1],cnt[1]-2):1;
for(int i=2;i<=n;i++) if(cnt[i]) ans=1ll*ans*f[i]%mod*Pow(cnt[i]*i+f[i],cnt[i]-1)%mod;
}
else if(cnt[2]){
ans=Pow(2*cnt[2],cnt[2]-1);
for(int i=3;i<=n;i++) if(cnt[i]) ans=1ll*ans*f[i]%mod*Pow(cnt[i]*i+f[i],cnt[i]-1)%mod;
}
printf("%d\n",ans);
}
}