题目描述:
给出n个点,求n个点的无向连通图
分析:
巨坑啊
经典题目,有两种方法:
总方案数-不合法方案
n n 个点的完全图有条边,显然就有 2C(n,2) 2 C ( n , 2 ) 种子图(枚举每条边是否选择)
设
f[i]
f
[
i
]
表示每个点都和点1相连的连通图的个数
假设1号点所在的连通块大小为
i
i
那么和1连通的这个点就有
C(n−1,i−1)
C
(
n
−
1
,
i
−
1
)
种选择,方案数为
C(n−1,i−1)∗f[i]
C
(
n
−
1
,
i
−
1
)
∗
f
[
i
]
其它 n−i n − i 个点间任意连边即可,方案数为 2C(n−i,2) 2 C ( n − i , 2 )
则有递推公式:
在代码实现上,基本上都是高精度
所以网上的代码都是什么Java,python之类的
直接求方案数
考虑去掉1号点时,2号点的情况
设此时2所在的连通块共有
i
i
个点
这个点和剩下的
n−i
n
−
i
个点分别处在两个不同连通块中(下面就要把这两个连通块连接在一起)
其方案数为
f[i],f[n−i]
f
[
i
]
,
f
[
n
−
i
]
,2号点需要在剩下的点(除去点1,点2)中选择
i−1
i
−
1
个点同处一个连通块,方案数为
C(n−2,i−1)
C
(
n
−
2
,
i
−
1
)
而这
i
i
个点与除去1的个点(这
n−i−1
n
−
i
−
1
个点都与点1连通)都需要通过1号点来连接,
所以与2号点连通的
i
i
个点与1号点至少有1条边连接,方案数为
故这样的情况总共有:
f[i]∗f[n−i]∗C(n−2,i−1)∗(2i−1)
f
[
i
]
∗
f
[
n
−
i
]
∗
C
(
n
−
2
,
i
−
1
)
∗
(
2
i
−
1
)
则有递推公式
这种思路xue微有点困难,而且还是需要用高精度
不过只需要高精加和高精乘即可(比第一种方法简单一点)
tip
方法二code
#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
int n,nn=2;
struct node{
int o[400],len;
node operator *(const node &a) const {
int L=(len+a.len);
node ans; ans.clear();
ans.len=L;
for (int i=1;i<=len;i++)
for (int j=1;j<=a.len;j++) {
ans.o[i+j-1]+=o[i]*a.o[j];
ans.o[i+j]+=ans.o[i+j-1]/10;
ans.o[i+j-1]%=10;
}
while (!ans.o[ans.len]) ans.len--;
return ans;
}
node operator +(const node &a) const {
int L=max(len,a.len);
int d=0;
node ans; ans.clear();
for (int i=1;i<=L;i++) {
ans.o[i]=o[i]+a.o[i]+d;
d=ans.o[i]/10;
ans.o[i]%=10;
}
ans.len=L;
if (d) ans.o[++ans.len]=d;
return ans;
}
void clear() {memset(o,0,sizeof(o));len=0;}
node get(ll x) {
node ans; ans.clear();
while (x) {
ans.o[++ans.len]=x%10;
x/=10;
}
return ans;
}
};
node f[51],c[51][51];
void prepare() {
c[0][0].len=1; c[0][0].o[1]=1;
for (int i=1;i<=50;i++) {
c[i][0].o[1]=1; c[i][0].len=1;
for (int j=1;j<=i;j++)
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
}
void solve(int n) {
node t;
for (int i=1;i<n;i++) {
t=t.get((1LL<<i)-1);
f[n]=f[n]+f[i]*f[n-i]*c[n-2][i-1]*t;
}
}
void print(int n) {
for (int i=f[n].len;i>=1;i--)
printf("%d",f[n].o[i]);
printf("\n");
}
int main()
{
f[1].o[1]=1; f[1].len=1;
f[2].o[1]=1; f[2].len=1;
nn=2;
prepare();
while (scanf("%d",&n)!=EOF&&n) {
if (n<=nn) {print(n);continue;}
for (int i=nn+1;i<=n;i++)
solve(i);
nn=n;
print(n);
}
return 0;
}