题目来源:http://poj.org/problem?id=1737
设f[n]表示n个点的连通图的个数。可以考虑n个点组成的所有图的总方案数减去不连通的方案数得到最终结果。
总方案数为2^n*(n-1)/2。
不连通的方案数:
考虑1号节点所处的联通块的节点个数,假设当前连通块内节点个数为k,那么该联通块的组成情况可能有f[k]*c[n-1][k-1]种,c表示组合数。其余节点共有n-k个,与连通块没有任何边相连,这n-k个点内部的组成情况可能有2^(n-k)*(n-k-1)/2种。
因此,f[n]=Σ2^n*(n-1)/2 - f[k]*c[n-1][k-1]*2^(n-k)*(n-k-1)。
数据很大,需要用高精度大整数。
代码:
import java.io.*;
import java.lang.*;
import java.rmi.*;
import java.util.*;
import java.math.*;
public class Main {
static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
static int nextInt() throws IOException {
in.nextToken();
return (int) in.nval;
}
static double nextDouble() throws IOException {
in.nextToken();
return in.nval;
}
static long nextLong() throws IOException {
in.nextToken();
return (long) in.nval;
}
static String next() throws IOException {
in.nextToken();
return in.sval;
}
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static BigInteger f[], c[][];
static BigInteger pow_mod(BigInteger a, long b) {
BigInteger ans = BigInteger.ONE;
while (b > 0) {
if ((b & 1) != 0) ans = ans.multiply(a);
a = a.multiply(a);
b >>= 1;
}
return ans;
}
public static void main(String[] args) throws IOException {
f = new BigInteger[51];
c = new BigInteger[51][51];
for (int i = 1; i <= 50; ++i) f[i] = BigInteger.ZERO;
for (int i = 1; i <= 50; ++i) {
for (int j = 0; j <= i; ++j) {
if (j == 0 || j == i || i == 1) c[i][j] = BigInteger.ONE;
else c[i][j] = c[i - 1][j].add(c[i - 1][j - 1]);
}
}
BigInteger TWO = new BigInteger("2");
for (int n = 1; n <= 50; ++n) {
for (int k = 1; k < n; ++k) {
f[n] = f[n].subtract(f[k].multiply(c[n - 1][k - 1].multiply(pow_mod(TWO, (n - k) * (n - k - 1) / 2))));
}
f[n] = f[n].add(pow_mod(TWO, n * (n - 1) / 2));
}
int x;
while (in.nextToken() != in.TT_EOF) {
x = (int) in.nval;
if (x == 0) break;
out.println(f[x]);
}
out.flush();
}
}