51Nod 1383&1048 整数分解为2的幂

题目

任何正整数都能分解成2的幂,给定整数N,求N的此类划分方法的数量。
V1: n 10 6 。要对答案模 1 e 9 + 7
V2: n 10 30 。将整个答案输出。

解题思路

考虑每一种划分方法的序列是怎样从一个空的序列变来的。
显然,它是要么 + 1 ,要么集体乘 2
因此很快乐地得出 F [ x ] = F [ x 1 ] + F [ x / 2 ] [ x % 2 == 0 ]
当然,如果要与 V 2 接轨,那么要换一种思路。
考虑将 n 转换为二进制,则 n = Σ   2 x
因此,如果知道每个 2 i 划分为2的幂的和的方案数,那么这题就很好做了。
对于某个 2 i 的一种划分方案,如果不是只有它本身一个数,一定可以把这些2的幂按照升序排序,然后分成两段,每一段的和都是 2 i 1
g [ i ] [ j ] 表示做完了 2 进制下的前 i 位,最大的数为 2 j 的方案数。
f [ i ] [ j ] 表示组成 2 i ,最大的数为 2 j 的方案数。
有人想到这样的转移方程:
g [ i ] [ j ] = Σ k = 0 j   g [ i 1 ] [ k ] f [ i ] [ j ]
f [ i ] [ j ] = Σ k = 0 j   f [ i 1 ] [ k ] f [ i 1 ] [ j ]
但是这样会算重。
为了避免这,所以强制让后面的数字大于等于 2 k
所以将后面的数字都除以 2 k 之后,可以得出这么一个方程:
g [ i ] [ j ] = Σ k = 0 j   g [ i 1 ] [ k ] f [ i k ] [ j k ]
f [ i ] [ j ] = Σ k = 0 j   f [ i 1 ] [ k ] f [ i 1 k ] [ j k ]
显然先预处理 f
然后套上高精度,再卡常,就可以通过此题了。
卡常很重要,就是拿个 l o n g   d o u b l e ,确定一下 l o g 2 ( n )
然而不知道为什么,高精度压8位WA了,压9位就没事?!

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100
#define M 150
#define mo 1000000000
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note{
    int w,a[M];
};note f[N][N],g[N][N],ans,n,c;
int i,j,k,l,ys,x,tot;
int xx;
char s[32];
double x1,x2,x3;
note operator + (const note &a,const note &b){
    c.w=a.w>b.w?a.w:b.w;
    memset(c.a,0,sizeof(c.a));
    int i;
    fo(i,1,c.w){
        c.a[i]+=a.a[i]+b.a[i];
        c.a[i+1]+=c.a[i]/mo;
        c.a[i]%=mo; 
    }
    if(c.a[c.w+1])c.w++;
    return c;
}
note operator * (const note &a,const note &b){
    memset(c.a,0,sizeof(c.a));
    int i,j;
    long long kk;
    fo(i,1,a.w)fo(j,1,b.w){
        kk=c.a[i+j-1]+1ll*a.a[i]*b.a[j];
        c.a[i+j]=c.a[i+j]+kk/mo;
        c.a[i+j-1]=kk%mo;
    }
    c.w=a.w+b.w;
    while(!c.a[c.w]&&c.w>1)c.w--;
    return c;
}
int main(){
    scanf("%s",s+1);
    l=strlen(s+1);
    fo(i,1,l){
        x1=x1*10+s[i]-'0';
    }
    x2=log(x1)/log(2);
    n.w=l/9+1;
    fo(i,1,l){
        int j=(l-i)/9;
        n.a[j+1]=n.a[j+1]*10+(s[i]-'0');
    }
    while(!n.a[n.w]&&n.w>1)n.w--;
    f[0][0].w=f[0][0].a[1]=1;
    fo(i,1,x2){
        fo(j,0,i-1)fo(k,0,j)
            f[i][j]=f[i][j]+f[i-1][k]*f[i-1-k][j-k];
        f[i][i].w=f[i][i].a[1]=1;
    }
    fo(i,0,x2){
        if(n.a[1]&1){
            tot++;
            if(tot==1){
                fo(j,0,i)g[tot][j]=f[i][j];
            }else{
                fo(j,0,i)fo(k,0,j)
                    g[tot][j]=g[tot][j]+g[tot-1][k]*f[i-k][j-k];
            }
        }
        ys=0;
        fd(j,n.w,1){
            x=n.a[j];
            n.a[j]=(n.a[j]+ys*mo)/2;
            ys=(ys*mo+x)&1;
        }
        while(!n.a[n.w]&&n.w>1)n.w--;
    }
    fo(i,0,99)ans=ans+g[tot][i];
    printf("%d",ans.a[ans.w]);
    fd(i,ans.w-1,1){
        xx=log(ans.a[i])/log(10)+1;
        fo(j,1,9-xx)putchar('0');
        printf("%d",ans.a[i]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/huangjingyuan107/article/details/80470385
今日推荐