【题目链接】http://poj.org/problem?id=1737
【题目大意】给定一个数n(1<=n<=50),求这n个点构成连通图的方法种数。
【分析】
贴下别人的部分题解:
利用补集转化思想, 若不考虑连通性,则有n个点的任意图有G(n)=2^C(n, 2)个. 只要求出不连通图的个数就知道答案F(n). 考虑含结点1的连通块C, |C| = k. 由于不连通,有k < n成立. 则有C(n - 1, k – 1)种方法选择另k-1个结点与结点1组成C. 所以有C(n - 1, k – 1) * F(k)种方法组成C. 剩下的结点组成一个任意图即可. 答案为:
(以上可以作为结论,以后可能用到)
我们整个代码过程,就是先求出组合数,然后是g[]数组,最后是f[]数组,求f[]数组过程中公式里的k是从1到n-1。由于数据量大,整个过程必须用到高精度。
【代码】
/*1737 Accepted 1420K 32MS C++ 5274B 2011-02-05 21:06:26 */
#include<stdio.h>
#include<string.h>
#include"bigint.h"
int f[52][200],g[52][200],c[52][52][200];
int Tmp[200],tmp[200],sum[200];
void Cal_comb(){//计算组合数;
int i,j;
copy(c[1][1],ONE),copy(c[1][0],ONE),copy(c[0][0],ONE);
for(i=2;i<50;i++){
copy(c[i][0],ONE), copy(c[i][i],ONE);
for(j=1;j<i;j++)
add(c[i-1][j-1],c[i-1][j],c[i][j]);
}
//PN(c[10][9]);
}
void Init_gn(){
int i,tim;
copy(g[1],ONE),add(ONE,ONE,g[2]),copy(tmp,g[2]);
for(i=3;i<=50;i++){
copy(sum,ONE);
tim=i*(i-1)/2; //幂次;
while(tim--){
copy(Tmp,ZERO);
mult(tmp,sum,Tmp);
copy(sum,Tmp);
}
copy(g[i],sum);
}
}
void Init_fn(){
int i,j;
copy(f[1],ONE), copy(f[2],ONE);
for(i=3;i<=50;i++){
copy(sum,ZERO);
for(j=1;j<i;j++){
mult(f[j],g[i-j],tmp);
mult(tmp,c[i-1][j-1],Tmp);
add(Tmp,sum,tmp);
copy(sum,tmp);
}
sub(g[i],sum,f[i]);
}
}
int main()
{
int n,i,j,k;
Cal_comb();
Init_gn();
Init_fn();
//PN(f[12]); printf("%d/n",f[12][0]); //93
//PN(g[12]); printf("%d/n",g[12][0]);
while(scanf("%d",&n),n)
PN(f[n]);
}