Urban Planning
\(\text{Problem}:\)Urban Planning
\(\text{Solution}:\)
将题目转换为求生成环的总数。
考虑未形成的环是由若干个存在出度为 \(0\) 点所在的树连接而成的。每棵树上可以随便选节点,连边方案是圆排列,所以选择 \(i\) 个树连通块形成环的总方案数为:
\((i-1)!\times \prod\limits_{k=1}^{i} siz_{k} \times (n-1)^{cnt-i}\)
\(cnt\) 指 \(-1\) 的个数,也是原图中树连通块的个数。
对于 \(i=1\) 的情况,由于每个点不能连到自己,所以方案数是 \((siz_{1}-1)\times (n-1)^{cnt-1}\)。
当然,现在需要对所有的 \(siz_{1},siz_{2}...siz_{k}\) 序列求出答案并求出总和,考虑求 \(\sum \prod\limits_{k=1}^{i} siz_{k}\)。记 \(F_{i,j}\) 表示前 \(i\) 个 \(-1\) 选了 \(j\) 个时的答案。转移为:
-
\(F_{i,0}=1\)
-
\(F_{i,j}=(F_{i-1,j}+F_{i-1.j-1}\times siz_{i})(j\geq 1)\)
最后用 \(F_{n,i}\) 统计答案即可。时间复杂度 \(O(n^{2})\)。
\(\text{Code}:\)
#include <bits/stdc++.h>
#pragma GCC optimize(3)
#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
using namespace std; const int N=5010, Mod=1e9+7;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
}
int n,p[N],Ans=1;
int f[N],siz[N];
inline void Init()
{
for(ri int i=1;i<=n;i++) f[i]=i, siz[i]=1;
}
inline int Find(int x) { return f[x]^x?f[x]=Find(f[x]):x; }
int h[N],fac[N+10];
int tot,cnt,id[N],sum,sz[N],F[N][N],book[N];
inline int ksc(int x,int p) { int res=1; for(;p;p>>=1, x=x*x%Mod) if(p&1) res=res*x%Mod; return res; }
signed main()
{
fac[0]=1;
for(ri int i=1;i<=N;i++) fac[i]=fac[i-1]*i%Mod;
n=read();
for(ri int i=1;i<=n;i++) p[i]=read();
Init();
int cir=0;
for(ri int i=1;i<=n;i++)
{
if(~p[i])
{
int fx=Find(i), fy=Find(p[i]);
if(fx==fy) { book[fx]++; continue; }
f[fx]=fy, siz[fy]+=siz[fx];
book[fy]|=book[fx];
}
}
for(ri int i=1;i<=n;i++) if(i==Find(i)) cir+=book[i];
for(ri int i=1;i<=n;i++)
{
int x=Find(i);
if(!id[x]) id[x]=++cnt, sz[cnt]=siz[x];
if(p[i]==-1) h[id[x]]++, sum++;
}
Ans=(n-cir)*ksc(n-1,sum)%Mod;
int pp=0;
for(ri int i=1;i<=cnt;i++) if(h[i]) sz[++pp]=sz[i];
cnt=pp;
F[0][0]=1;
for(ri int i=1;i<=cnt;i++)
{
for(ri int j=0;j<=i;j++)
{
F[i][j]=F[i-1][j];
if(j) F[i][j]=(F[i][j]+F[i-1][j-1]*sz[i])%Mod;
}
}
for(ri int i=1;i<=cnt;i++)
{
int gg=F[cnt][i];
if(i==1) gg=(gg-cnt+Mod)%Mod;
Ans=(Ans-fac[i-1]*gg%Mod*ksc(n-1,cnt-i)%Mod+Mod)%Mod;
}
printf("%lld\n",Ans);
return 0;
}
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。