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;
}
posted @ 2021-03-01 15:50  zkdxl  阅读(154)  评论(0编辑  收藏  举报