n个点带标号的连通图数量
计数思想:构造连通图f(n)、不连通图g(n)、总图h(n),其中h(n)可直接计数,且h(n)=g(n)+f(n)。再寻找f(n)、g(n)、h(n)三者的一个等式关系即可
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<map>
#include<vector>
#include<queue>
#define maxn 50
#define maxm 1000
#define ll long long
#define sf scanf
#define pf printf
#define mem(a,b) memset(a,b,sizeof(a))
const ll mod=1000000007;
#define clr(x) memset(x,0,sizeof(x))
using namespace std;
int n;
char f[maxn+10][500],h[maxn+10][500],g[maxn+10][500];
char c[maxn+10][maxn+10][500];
void strrev1(char *a,int len){//g++和c++都无法编译出strrev函数,因此单独写出一个反转函数
char b[600];
mem(b,0);//len长度0...len-1
for(int j=0;j<len;j++){
b[j]=a[len-1-j];
}
for(int i=0;i<len;i++){
a[i]=b[i];
}
}
void add(char *c, char *a, char *b)//c=a+b <span style="font-family: Arial, Helvetica, sans-serif;">! add(a,b,a)---错误!!! c在运算中要用到a和b</span>
{
int len_a = strlen(a);
int len_b = strlen(b);
int len_c = max(len_a, len_b);
memset(c, 0, (len_c+2)*sizeof(c[0]));
for(int i=len_a-1, j=len_c; i>=0; --i, --j)
c[j] += a[i] - '0';
for(int i=len_b-1, j=len_c; i>=0; --i, --j)
c[j] += b[i] - '0';
for(int i=len_c; i>0; c[i]+='0', --i)
if(c[i] > 9)
{
c[i] -= 10;
++c[i-1];
}
if(!c[0])
{
for(int i=0; i<len_c; ++i)
c[i] = c[i+1];
c[len_c] = 0;
}
else
c[0] = '1';
}
void multiply(char *c,char *a,char *b)//c=a*b
{
int ca,cb,s[500];
ca=strlen(a);//a
cb=strlen(b);
for (int i=0;i<ca+cb;i++) s[i]=0; // 每个元素赋初值0
for (int i=0;i<ca;i++)
for (int j=0;j<cb;j++)
s[i+j+1]+=(a[i]-'0')*(b[j]-'0');// 逐位计算
for (int i=ca+cb-1;i>=0;i--) // 统一实现进位操作
if (s[i]>=10)
{
s[i-1]+=s[i]/10;
s[i]%=10;
}
int i=0,j=0;
while(s[i]==0) i++; // 跳过头部0元素
for (j=0;i<ca+cb;i++,j++) c[j]=s[i]+'0';
c[j]='\0';
}
void sub(char strc[500],char stra[500],char strb[500])//c=a-b
{
char a[500],b[500],c[600];
int aa[500],bb[500],cc[500];
int i,len_a,len_b,len;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
mem(aa,0),mem(bb,0),mem(cc,0);
strcpy(a,stra);strcpy(b,strb);
len_a=strlen(a);len_b=strlen(b);
strrev1(a,len_a);strrev1(b,len_b);
for(i=len_a-1;i>=0;i--)
aa[i]=a[i]-'0';
for(i=len_b-1;i>=0;i--)
bb[i]=b[i]-'0';
len=len_a;
for(i=0;i<len;i++)
{
if(aa[i]<bb[i])
{
aa[i+1]--;
aa[i]=aa[i]+10;
}
cc[i]=aa[i]-bb[i];
}
for(i=0;i<len;i++)
c[i]=cc[i]+'0';
for(i=len-1;i>=0;i--)
if(c[i]!='0')
{
c[i+1]='\0';
break;
}
strrev1(c,i+1);
strcpy(strc,c);
}
void CC(){//计算组合数C(m,n)
clr(c);
for(int i=0;i<=50;i++){
c[i][0][0]='1',c[i][i][0]='1';
c[i][0][1]=c[i][i][1]='\0';
for(int j=1;j<i;j++){
add(c[i][j],c[i-1][j-1],c[i-1][j]);
}
}
}
void quick_pow(char *c,char *a,ll b){//c=a^b
while(b){
if(b&1) multiply(c,c,a);
b/=2;
multiply(a,a,a);
}
}
void init(){
clr(f),clr(h),clr(g);
h[0][0]=f[0][0]='1';
h[0][1]=f[0][1]='\0';
char tmp[500],tmp2[500];
for(int i=1;i<=50;i++){
for(int j=1;j<i;j++){
clr(tmp),clr(tmp2);
multiply(tmp,f[j],h[i-j]);
multiply(tmp,c[i-1][j-1],tmp);
add(tmp2,g[i],tmp);
strcpy(g[i],tmp2);
}
char base[500];
clr(base);
base[0]='2',base[1]='\0';
clr(tmp);
tmp[0]='1',tmp[1]='\0';
quick_pow(tmp,base,i-1);
multiply(h[i],tmp,h[i-1]);//求h[i]
sub(f[i],h[i],g[i]);//求f[i]
}
}
int main(){
CC();
init();
while(scanf("%d",&n)!=EOF){
if(!n) break;
printf("%s\n",f[n]);
}
}