ZOJ 3319 有向图加边成环方法数

题意:

给定n个点 以及有向边的矩阵。

加一些有向边使得 每个点 入度=出度= 1; 的方法数 (原意是每个点只存在于一个环 (一个环至少2个点

显然图中的点我们分为4种 (入度,出度) (0,0) (0,1) (1,0) (1,1) (若有其他类的点则图不合法,直接输出0)


而且 Num(0,1) == Num(1,0) 

因为这种点只会在链两端各产生一个

剩下的则是孤立点 (0,0) (因为(1,1)点是在链内部或者环中,没有必要考虑)

对于孤立点,我们需要用至少n-1条链相连。

因此我们在其中选择 大于等于n-1条链,剩下的链自成环。



#include<stdio.h>
#include<iostream>
#include<math.h>
#include<algorithm>
#include<vector>
#include<string.h>
using namespace std;
#define N 105
#define mod 10000007
#define ll long long
ll in[N], out[N], n;

vector<ll>E[N];
ll D[N], C[N][N];
ll dfs(ll u){
	if(!E[u].size())return 1;
	return 1+dfs(E[u][0]);
}
void init(){
	for(ll i = 0; i <= n; i++)E[i].clear();
	memset(in, 0, sizeof in);
	memset(out, 0, sizeof out);
}
int main(){
	ll i, j;
	D[0] = 1, D[1] = 0;
	for(i=2;i<N;i++)D[i] = (i-1)*(D[i-2]+D[i-1]), D[i]%=mod;
	for(i = 0; i < N; i++)
	{
		C[0][i] = 0;
		C[i][0] = 1;
	}
	for(i = 1; i < N; i++)
	{
		for(j = 1; j < N; j++)
			C[i][j] = (C[i-1][j] + C[i-1][j-1])%mod;
	}
	while(scanf("%lld",&n),n){
		init();
		bool yes = false;
		for(i=1;i<=n;i++){
			char s[N];scanf("%s",s);
			for(j=1;s[j-1];j++) if(s[j-1]=='Y')
			{
				if(i==j)yes = true;
				in[j]++, out[i]++;
				E[i].push_back(j);
			}
		}
		for(i = 1; i <= n; i++)if(in[i]>=2 || E[i].size()>=2)yes = true;
		if(yes || n==1){puts("0");continue;}

		ll st[N], top = 0;
		for(i = 1; i <= n; i++) if(in[i]==0)st[top++] = dfs(i);
		sort(st, st+top);
		ll num = upper_bound(st, st+top, 1)-st;
		ll left = top - num;
		ll ans = 0;
		for(i = num; i <= top; i++)
			ans += C[left][i-num]*D[i], ans %= mod;
		printf("%lld\n",ans%mod);
	}
	return 0;
}
/*
2
NN
NN
2
NY
YN
3
NNN
NNN
NNN
3
NYY
NNN
NNN
0

*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值