大致题意:
A set ρ of pairs (a, b) of elements of some set A is called a binary relation on set A. For two elements a and b of the set A we say that they are in relation ρ, if pair , in this case we use a notation .
Binary relation is equivalence relation, if:
给出了等值Binary relation的定义,给定数的全集{1,2,3...,n}求有多少个ρ 集合满足等值关系中的2,3两条而不满足第一条
题意真是难理解...
思路:
只要有某个元素a满足了2,3两条,就一定由 和 推出,所以这个元素就满足了第一条
同理如果这个集合中的每个元素满足2,3条那么每个元素都满足第一条也就是此集合成为了等值Binary relation。
所以为了不成为等值Binary relation,必须至少有一个元素x不满足所有关系,也就是不参与构成了某个二元组,除x外的元素恰好构成了等值Binary relation。
所以本题的答案就是 sigama(i:0,n-1){ C(n,i)*(这i元素构成的等值Binary relation的个数) }
现在问题变成i个元素可以构成多少个等值Binary relation
这个问题可以用递推解决,求dp[i],对于第i个元素必须满足这三个条件才行,所以必须让第i个元素与i-1个元素中的k个元素“混合”才行
比如{(a~b),(b~a),(a~a)}混入c后要满足等值Binary relation则变成{(a~b),(b~a),(b~c),(c~b),(a~c),(c~a),(a~a),(b~b),(c~c)}只有唯一的混合结果
所以dp[i] = sigma(k:0,i-1){ C(i-1,k)*dp[i-1-k] } = sigma(k:0,i-1){ C(i-1,i-1-k)*dp[i-1-k] } = sigma(k:0,i-1){ C(i-1,k)*dp[k] }
会发现这就是Bell数的递推公式
所以求“i个元素可以构成多少个等值Binary relation”这个问题等价与“i的集合的划分方法的数目",例如:
集合{a, b, c}有5种不同的划分方法:
每个块中都是前面所说的混合构造成等值Binary relation,所以知道bell数的会很容易解决此问题,到此问题已经圆满解决
//#pragma comment(linker, "/STACK:1024000000,1024000000") #include <iostream> #include <cstring> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> #include <string> #include <vector> #include <cstdio> #include <ctime> #include <bitset> #include <algorithm> #define SZ(x) ((int)(x).size()) #define ALL(v) (v).begin(), (v).end() #define foreach(i, v) for (__typeof((v).begin()) i = (v).begin(); i != (v).end(); ++ i) #define reveach(i, v) for (__typeof((v).rbegin()) i = (v).rbegin(); i != (v).rend(); ++ i) #define REP(i,n) for ( int i=1; i<=int(n); i++ ) #define rep(i,n) for ( int i=0; i< int(n); i++ ) using namespace std; typedef long long ll; #define X first #define Y second typedef pair<int,int> pii; template <class T> inline bool RD(T &ret) { char c; int sgn; if (c = getchar(), c == EOF) return 0; while (c != '-' && (c<'0' || c>'9')) c = getchar(); sgn = (c == '-') ? -1 : 1; ret = (c == '-') ? 0 : (c - '0'); while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0'); ret *= sgn; return 1; } template <class T> inline void PT(T x) { if (x < 0) { putchar('-'); x = -x; } if (x > 9) PT(x / 10); putchar(x % 10 + '0'); } const int N = 4000+123; const int MOD = 1e9+7; int bell[N][N]; int C[N][N]; int main(){ bell[0][0] = 1; REP(i,N-10) REP(j,i){ if(j == 1) bell[i][j] = bell[i-1][i-1]; else bell[i][j] = (bell[i][j-1]+bell[i-1][j-1])%MOD; } REP(i,N-10) { C[i][i] = C[i][0] = 1; REP(j,i-1) C[i][j] = (C[i-1][j-1]+C[i-1][j])%MOD; } int n; while(~scanf("%d",&n)){ ll ans = 0; rep(i,n) ans = (ans+C[n][i]*(ll)bell[i][i])%MOD; PT(ans),puts(""); } }
作为弱渣第一次遇过bell数,现在补充记录一些常用的基础知识:
Stirling数,第一类有点难理解不在此记录
第二类Stirling数是个元素的划分成k个非空集合的方法数目。常用的表示方法有。
证明递推式:考虑第n个物品,n可以单独构成一个非空集合,此时前n-1个物品构成k-1个非空的不可辨别的集合,有种方法;也可以前n-1种物品构成k个非空的不可辨别的集合,第n个物品放入任意一个中,这样有种方法。
Bell数:n的集合的划分方法的数目。
由定义可以得出它和第二类stirling数的关系:,即划分成k个非空集合
有两个递推关系可以求bell数:
1.
上述组合公式的证明:
可以这样来想,B_{n+1}是含有n+1个元素集合的划分的个数,考虑元素
假设他被单独划分到一类,那么还剩下n个元素,这种情况下划分个数为;
假设他和某一个元素被划分为一类,那么还剩下n-1个元素,这种情况下划分个数为 ;
假设他和某两个元素被划分为一类,那么还剩下n-2个元素,这种情况下划分个数为 ;
依次类推,得到了上述组合公式
2.根据贝尔三角形递推(较方便形象)
用以下方法建构一个三角矩阵(形式类似杨辉三角形):
结果如下:(OEIS:A011971)
每行首项是贝尔数。
摘自:
https://zh.wikipedia.org/wiki/%E8%B4%9D%E5%B0%94%E6%95%B0
https://zh.wikipedia.org/wiki/%E6%96%AF%E7%89%B9%E7%81%B5%E6%95%B0