poj1737

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]);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值