【51NOD 1048】【51NOD 1383】整数分解为2的幂 V2

Description

任何正整数都能分解成2的幂,给定整数N,求N的此类划分方法的数量!
比如N = 7时,共有6种划分方法。

7=1+1+1+1+1+1+1
=1+1+1+1+1+2
=1+1+1+2+2
=1+2+2+2
=1+1+1+4
=1+2+4

Solution

gi,j,k 表示一个序列组成 2i 这个数,这个序列中最大的数为 2j ,最小的为 2k 的方案数;
fi,j 表示已经做完了输入的数的后i位二进制,序列中最大的数为 2j 的方案数;

gi,j,k=l=kjgi1,j,l(t=klgi1,t,k)

fi,j=k=1jgi,j,k(l=1kfi1,l)

有两个很显然可以用前缀和,
复杂度: O(n4)

优化:
发现有性质

gi,j,k=gi1,j1,k1=gi2,j2,k2=......

所以只要记录 gi,j 表示由最大 2j 最小 20 组成 2i 的方案数即可,

注意题目没有mo,要用高精度!
复杂度: O(n3)

Code

标程只加了 gi,j,k=gi1,j1,k1 的优化,所以很丑QwQ而且还要卡常QwQ:
这里写图片描述

#include <cstdio>
#include <cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define max(q,w) ((q)>(w)?(q):(w))
using namespace std;
typedef long long LL;
const int N=102,YW=1000000000,YW1=9;
int m,n;
int a[N],z[N];
struct qqww
{
    int n;
    int a[151];
}f[2][N],g[2][N][N],LIN,YI,t;
int ss(int w)
{
    if(!a[0])return 0;
    int q=0;
    fod(i,a[0],1)
    {
        a[i]+=q*10;
        q=a[i]&1;a[i]>>=1;
    }
    if(!a[a[0]])a[0]--;
    int OU=q;
    q=ss(w+1)+1;
    z[w]=OU;
    return q;
}
qqww s;
qqww operator * (qqww a,qqww b)
{
    if((a.n<2&&a.a[1]==0))return a;
    if((b.n<2&&b.a[1]==0))return b;
    fo(i,1,s.n)s.a[i]=0;
    s.n=a.n+b.n-1;
    fo(i,1,a.n)
        fo(j,1,b.n)
        {
            LL t=s.a[i+j-1]+(LL)a.a[i]*b.a[j];
            s.a[i+j-1]=t%YW;
            s.a[i+j]+=t/YW;
        }
    while(s.a[s.n+1])
    {
        s.n++;
        s.a[s.n+1]+=s.a[s.n]/YW;
        s.a[s.n]%=YW;
    }
    return s;
}
qqww operator + (qqww a,qqww b)
{
    a.n=max(a.n,b.n);
    fo(i,1,a.n)
    {
        a.a[i]+=b.a[i];
        while(a.a[i]>=YW)
        {
            a.a[i+1]++;
            a.a[i]-=YW;
        }
    }
    while(a.a[a.n+1])
    {
        a.n++;
        a.a[a.n+1]+=a.a[a.n]/YW;
        a.a[a.n]%=YW;
    }
    return a;
}
int main()
{
    char ch;
    for(char ch=getchar();ch<='9'&&ch>='0';ch=getchar())a[++a[0]]=ch-48;
    fo(i,1,a[0]/2)
    {
        int t=a[i];
        a[i]=a[a[0]-i+1];
        a[a[0]-i+1]=t;
    }
    n=ss(1);
    g[0][1][1].n=g[0][1][1].a[1]=1;
    fo(i,1,n)f[0][i].n=f[0][i].a[1]=1;
    bool nw=1,nwg=1;
    fo(i,2,n)
    {
        fo(I,1,g[nwg][i][i].n)g[nwg][i][i].a[I]=0;
        g[nwg][i][i].n=g[nwg][i][i].a[1]=1;
        fo(I,1,g[nwg][1][1].n)g[nwg][1][1].a[I]=0;
        g[nwg][1][1].n=g[nwg][1][1].a[1]=1;
        fo(j,2,i-1)fo(k,2,j)
        {
            fo(I,1,max(g[nwg][j][k].n,g[!nwg][j-1][k-1].n))g[nwg][j][k].a[I]=g[!nwg][j-1][k-1].a[I];
            g[nwg][j][k].n=g[!nwg][j-1][k-1].n;
        }
        fo(j,1,i-1)
        {
            fo(I,1,max(t.n,g[!nwg][1][1].n))t.a[I]=g[!nwg][1][1].a[I];
            t.n=g[!nwg][1][1].n;
            g[nwg][j][1]=(g[!nwg][j][1]*t);
            fo(k,2,j)
            {
                t=t+g[!nwg][k][1];
                g[nwg][j][1]=g[nwg][j][1]+g[!nwg][j][k]*t;
            }
        }
        if(z[i])
        {
            fo(j,1,n)
            {
                fo(I,1,max(f[nw][j].n,f[nw][j-1].n))f[nw][j].a[I]=f[nw][j-1].a[I];
                f[nw][j].n=f[nw][j-1].n;
                fo(k,1,n)
                    f[nw][j]=f[nw][j]+g[nwg][j][k]*f[!nw][k];
            }
            nw=!nw;
        }
        nwg=!nwg;
    }
    printf("%d",f[!nw][n].a[f[!nw][n].n]);
    fod(i,f[!nw][n].n-1,1)printf("%09d",f[!nw][n].a[i]);
    printf("\n");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值