UVa 11174 Stand in a Line(递推+排列组合+逆元)

思路:

1.我们首先根据父子关系可以建立一棵树(或者森林),我们设立一个虚根连接森林中所有树的根结点,那么一颗树就建好了,我们标记虚根为序号0
2.我们设f(i)f(i)表示以ii为根节点的树,这些人的排列总数,我们设s(i)s(i)表示以ii为根结点的树的结点数量,根据排列组合的知识我们可以得到
f(i)=(f(c))(s(i)1)!s(c)!f(i)= \frac{(\prod f(c))*(s(i)-1)!}{\prod s(c)!}
3.这一步需要一些抽象想象能力,我们从根结点开始用公式往下计算,一直算到叶子结点,可以发现所有非根结点的结点i,都有一次(s(i)-1)!在分子上,都有一次s(i)!在分母上,于是就可以上下抵消只剩下一个s(i)在分母上,最后我们可以算得
Answer=f(0)=n!s(1)s(2)...s(n)mod(1e9+7)Answer=f(0)=\frac{n!}{s(1)*s(2)*...*s(n)}mod (1e9+7)
至于计算s(i),一次dfs就可以解决了;
4.既然涉及到除法取模问题,那么就需要求逆元计算了,方法有扩展欧几里得和费马小定理,不会的朋友可以自行学习,这里就不赘述了~

代码:

#include<bits/stdc++.h>
using namespace std;
#define pt(a) cerr<<a<<"---\n"
typedef long long LL;
const int maxn=40000+5;
const LL mod=1e9+7;
int m;
vector<int> v[maxn];
LL n,s[maxn],tag[maxn];
void extgcd(int a,int b,int& x,int& y){
	if(b==0){x=1,y=0;return;}
	extgcd(b,a%b,x,y);
	int tmp=x;x=y;y=tmp-(a/b)*y;
}
int mod_inv(int a,int m){
	int x,y;
	extgcd(a,m,x,y);
	return (m+x%m)%m;
}
void dfs(int root){
	s[root]=1;
	if(v[root].size()) for(auto e:v[root]) dfs(e),s[root]+=s[e];
}
void solve(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) tag[i]=1;
	for(int i=0;i<m;i++){
		int a,b; cin>>a>>b;
		v[b].push_back(a);
		tag[a]=0;
	}
	for(int i=1;i<=n;i++) if(tag[i]) dfs(i);
	LL res=1ll;
	for(LL i=2;i<=n;i++) res*=i,res%=mod;
//	for(int i=1;i<=n;i++) pt(s[i]);
	for(int i=1;i<=n;i++) res*=mod_inv(s[i],mod),res%=mod;
	cout<<res<<'\n';
}
void clear(){
	for(int i=0;i<=n;i++){
		v[i].clear();
		s[i]=tag[i]=0;
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(NULL);
//	freopen("Arutoria.txt","r",stdin);
	int t; cin>>t;
	while(t--) solve(),clear();
	return 0;
}

posted @ 2019-12-04 16:06  YuhanのBlog  阅读(148)  评论(0编辑  收藏  举报