链接
http://poj.org/problem?id=1737
大意
给定 n n n个点的无向联通图有多少个,节点有标号
思路
首先 x j b xjb xjb乱想,我们就能想到 d p dp dp,然后这题显然是一个计数类 d p dp dp
因为是有标号的,所以一定和组合数有关,先算出组合再说
n
n
n个点的无向图总数显然是
2
n
×
(
n
−
1
)
2
2^{\frac{n\times(n-1)}{2}}
22n×(n−1)
因为 N N N个点的无向图最多有 n × ( n − 1 ) 2 \frac{n\times(n-1)}{2} 2n×(n−1)条边,每条边可有可无,所以是这个数
接下来计算 n n n个点不连通的数量,一张不连通的无向图必定有若干个联通快构成,所以,我们有 C n − 1 k − 1 C_{n-1}^{k-1} Cn−1k−1中选法。剩余 n − k n-k n−k个节点勾成任意
无向图,有 2 ( N − k ) × ( n − k − 1 ) 2 2^{\frac{(N-k)\times(n-k-1)}{2}} 22(N−k)×(n−k−1)种方法
就得到了方程
f
[
i
]
=
2
i
×
(
i
−
1
)
2
−
∑
j
=
1
i
−
1
f
[
j
]
×
C
i
−
1
j
−
1
×
2
(
i
−
j
)
×
(
i
−
j
−
1
)
2
f[i]=2^{\frac{i\times(i-1)}{2}}-\sum_{j=1}^{i-1}f[j]\times C_{i-1}^{j-1}\times 2^{\frac{(i-j)\times(i-j-1)}{2}}
f[i]=22i×(i−1)−j=1∑i−1f[j]×Ci−1j−1×22(i−j)×(i−j−1)
由于数据太大,要用高精度,然后再打一个压位高精即可,因为我们不确定高精乘法的位数,所以我们可以用vector去模拟高精乘
代码
#include<cstdio>
#include<vector>
#include<algorithm>
#define ymw 10000
using namespace std;
typedef unsigned long long ull;
int n;
vector<ull>tri[50],c[50],f[50];
inline void write(ull x){if(x>9)write(x/10);putchar(x%10+48);}//输出优化
int main()
{
for(register int i=0;i<50;i++) c[i].push_back(1);//一开始每个数默认为1
for(register int i=0;i<50;i++)
for(register int j=1;j<=i+1>>1;j++) c[i].push_back(c[i][j-1]*(i-j+2)/j);//算组合数
tri[0].push_back(1);//表示一张图中无向图的个数
for(register int i=1;i<50;i++)
{
tri[i]=tri[i-1]; ull g=0,s;
for(register int j=0;j<tri[i].size();j++)//高精乘单精
{
s=tri[i][j]*(1ll<<i)+g;
g=s/ymw; tri[i][j]=s%ymw;
}
while (g) tri[i].push_back(g%ymw),g/=ymw;//处理进位
}
f[0].push_back(1);//初始化f
for(register int i=1;i<50;i++)
{
f[i]=tri[i];//一开始就是这个玩意
for(register int j=0;j<i;j++)
{
vector<ull>t,t1; t=tri[i-j-1]; t1.clear(); ull g=0,s;
for(register int p=0;p<t.size();p++)//然后高精乘单精
{
s=t[p]*c[i-1][min(j,i-j)]+g;
g=s/ymw; t[p]=s%ymw;
}
while(g)t.push_back(g%ymw),g/=ymw;
for(register int p1=0;p1<t.size();p1++)
for(register int p2=0;p2<f[j].size();p2++)//高精乘高精
{
if(t1.size()==p1+p2) t1.push_back(t[p1]*f[j][p2]);else t1[p1+p2]+=t[p1]*f[j][p2];
if(t1.size()==p1+p2+1) t1.push_back(t1[p1+p2]/ymw);else t1[p1+p2+1]+=t1[p1+p2]/ymw;
t1[p1+p2]%=ymw;
}
g=0;
while(t1.size()<f[i].size()) t1.push_back(0);//记得这里要补0,不然访问无效内存
for(register int p=0;p<f[i].size();p++)//高精减高精
if(f[i][p]>=t1[p]+g) f[i][p]-=t1[p]+g,g=0;else f[i][p]+=ymw-t1[p]-g,g=1;
}
}
while(scanf("%d",&n)==1&&n)//输入
{
for (register int j=f[n-1].size()-1;j>=0;j--)
{
ull k=ymw/10; if (j<f[n-1].size()-1)
while(k>f[n-1][j]&&k>1) putchar('0'),k/=10;//输出
write(f[n-1][j]);
}
putchar(10);
}
return 0;
}