CodeForces-568B Symmetric and Transitive

题目描述

要求找出 \(n\) 个元素中满足对称性,传递性,但不满足自反性的所有二元关系种数。

有一个集合,它的元素是二元关系\((x,y)\),其中\(x,y\)的范围是\([1,n]\)。这个集合满足如下性质:
1.如果这个集合里有\((x,y)\),那么一定有\((y,x)\)
2.如果这个集合里同时有\((x,y)\)\((y,z)\),那么一定有\((x,z)\)。注意,在这条性质里,\(x,y,z\)可以相等
3.这个集合不同时包含\((1,1),(2,2),……(n,n)\)
问这个集合有多少种组成方式。
(当\(n=3\)时,有\(10\)种方式,为∅, {(1,1)} , {(2,2)} , {(3,3)} , {(1,1),(2,2)} , {(1,1),(3,3)} , {(2,2),(3,3)} , {(1,2),(2,1),(1,1),(2,2)} , {(1,3),(3,1),(1,1),(3,3)} , {(2,3),(3,2),(2,2),(3,3)})。

Input

输入 \(n\) ,表示有 \(n\) 个元素, \((1<=n<=4000)\).

Output

输出满足对称性,传递性,但不满足自反性的所有二元关系种数。

注意,答案可能很大,需要模 1e9+7,即1000000007

Sample Input

1
2
3

Sample Output

1
3
10

首先,我们发现,若一个集合中有二元关系\((x,y)\),则就一定有\((y,x)\),有\((x,x)\)\((y,y)\)

也就是说,一个集合中所有的元素必定会形成\((1,1)\),\((2,2)\)......这样的关系。

而题目中要求集合不能同时包含\((1,1),(2,2),……(n,n)\),实际上就是说集合中不能包含\(1-n\)所有的元素。

而这个我们可以利用贝尔数组解决。

定义:\(dp[i][j]\)表示前\(i\)个数分成\(j\)个集合的方案数。

考虑转移:对于第\(i\)个元素,我们有两种决策:

1.我们将其放入前面已经有的\(j\)个集合中。

2.我们们新建一个集合,将其放入。

所以我们有:\(dp[i][j]=dp[i-1][j-1]+j*dp[i-1][j]\)

而我们要求的答案是\(n\)个元素不能在同一个集合的方案数。

对于我们要求的答案,我们可以转换成,在\(n\)个数中选了\(i\)个数,这\(i\)个数分割成若干个集合,而剩下的\(n-i\)个数独立成为一个集合的方案数,\((i!=n)\)

则答案就为\(\sum_{i=1}^{n-1}C(n,i)*bell(i)\)

代码如下

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define reg register
#define Raed Read
#define debug(x) cerr<<#x<<" = "<<x<<endl;
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
#define erep(i,G,x) for(int i=(G).Head[x]; i; i=(G).Nxt[i])

inline int Read(void) {
	int res=0,f=1;
	char c;
	while(c=getchar(),c<48||c>57)if(c=='-')f=0;
	do res=(res<<3)+(res<<1)+(c^48);
	while(c=getchar(),c>=48&&c<=57);
	return f?res:-res;
}

template<class T>inline bool Min(T &a,T const&b) {
	return a>b ?a=b,1:0;
}
template<class T>inline bool Max(T &a,T const&b) {
	return a<b?a=b,1:0;
}

const int N=4e3+5,M=3e5+5,mod=1e9+7;

bool MOP1;

int dp[N][N],F[N],Fac[N];

inline int Pow(int x) {
	int res=1,y=mod-2;
	while(y) {
		if(y&1)res=(res*x)%mod;
		x=(x*x)%mod,y>>=1;
	}
	return res;
}

inline int C(int x,int y) {
	if(!x)return 1;
	return (Fac[y]*Pow((Fac[x]*Fac[y-x])%mod))%mod;
}

bool MOP2;

inline void _main(void) {
	dp[0][0]=1,F[0]=1,Fac[0]=1;
	rep(i,1,4000)Fac[i]=(Fac[i-1]*i)%mod;
	rep(i,1,4000)rep(j,1,4000) {
		dp[i][j]=(dp[i-1][j-1]+j*dp[i-1][j])%mod;
		F[i]=(F[i]+dp[i][j])%mod;
	}
	int n;
	while(~scanf("%lld",&n)) {
		int Ans=0;
		ret(i,0,n)Ans=(Ans+(C(i,n)*F[i])%mod)%mod;
		printf("%lld\n",Ans);
	}
}

signed main() {
	_main();
	return 0;
}
posted @ 2019-08-29 14:46  dsjkafdsaf  阅读(205)  评论(0编辑  收藏  举报