[51nod1383&1048]整数分解为2的幂
来源:互联网 发布:matlab编程题目 编辑:程序博客网 时间:2024/06/16 14:11
题目大意
任何正整数都能分解成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
最简单的方法
首先很容易想到用DP做。
设f[i][j]表示组成的数是i,方案中最大的数是
为了避免重复,f[i][j]转移时枚举下一个数必须大于等于
那么可以得出:
注意到第一维是和k无关的,配合前缀和优化即可做到
相对巧妙的做法
状态太大了,可不可以减去一维?
设f[i]表示组成i的方案数。如果还是枚举下一个数来转移,那么时间复杂度没变。
那么考虑换一种转移方式。先给出方程:
f[i-1]转移到f[i]表示加入了一个1,f[i/2]则表示f[i/2]的分解方案中,全部数乘2。这样转移也保证了加进来数的顺序是从大到小的,故不会重复。
时间复杂度
跑得很快的做法
如果n很大怎么办?
那样就要挖掘一下分解方案的性质了。
- 对于一个数n,假设它二进制下有m个1,分别是第a1,a2…am位。对于n的任意一种分解方案,把所有2的幂升序排序,然后可以划分成m段,其中第i段的和是
2ai - 对于一个数
2i 的一种划分方案,如果不是只有它本身一个数,一定可以把这些2的幂升序排序,然后分成两段,每一段的和都是2i−1
证明并不难。有了这些性质,就可以设新的状态了。
首先设g[i][j]表示做完了前i段(即n二进制下的前i个1位),最大的数是
再设一个辅助数组f[i][j],表示组成
接下来枚举第i-1段的最大数是多少,假设是
其中i-k,j-k的意义在于:要控制后面的数都大于等于
f[i][j]的转移类似
时间复杂度
#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int N=99,M=170,mo=1e9;typedef long long LL;char c[N];int tot;struct num{ int w,a[M];}f[N+5][N+5],ans,n,p,t,s[N+5],g[N+5][N+5];num operator + (const num &a,const num &b){ t.w=max(a.w,b.w); //t.a[1]=0; memset(t.a,0,sizeof(t.a)); for (int i=1;i<=t.w;i++) { t.a[i]+=a.a[i]+b.a[i]; if (t.a[i]>=mo) { t.a[i+1]=1; t.a[i]-=mo; }//else t.a[i+1]=0; } if (t.a[t.w+1]>0) t.w++; //memset(t.a+t.w+1,0,sizeof(t.a+t.w+1)); return t;}num operator * (const num &a,const num &b){ memset(t.a,0,sizeof(t.a)); for (int i=1;i<=a.w;i++) { for (int j=1;j<=b.w;j++) { t.a[i+j]+=(t.a[i+j-1]+(LL)a.a[i]*b.a[j])/mo; t.a[i+j-1]=(t.a[i+j-1]+(LL)a.a[i]*b.a[j])%mo; } } for (t.w=a.w+b.w;t.w>1 && t.a[t.w]==0;t.w--); return t;}int main(){ freopen("data.out","w",stdout); scanf("%s",c+1); int l=strlen(c+1); n.w=l/9+1; for (int i=1;i<=l;i++) { int j=(l-i)/9; n.a[j+1]=n.a[j+1]*10+c[i]-48; } f[0][0].w=f[0][0].a[1]=1; for (int i=1;i<=N;i++) { for (int j=0;j<i;j++) { for (int k=0;k<=j;k++) { 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; } tot=0; for (int i=0;i<=N;i++) { if (n.a[1]&1) { tot++; if (tot==1) { for (int j=0;j<=i;j++) g[1][j]=f[i][j]; }else { for (int j=0;j<=i;j++) { for (int k=0;k<=j;k++) { g[tot][j]=g[tot][j]+g[tot-1][k]*f[i-k][j-k]; } } } } int r=0; for (int j=n.w;j;j--) { int t=n.a[j]; n.a[j]=((LL)r*mo+t)/2; r=((LL)r*mo+t)%2; } } for (int i=0;i<=N;i++) ans=ans+g[tot][i]; printf("%d",ans.a[ans.w]); for (int i=ans.w-1;i;i--) { for (int j=1e8;j;j/=10) { printf("%d",ans.a[i]/j); ans.a[i]%=j; } } printf("\n"); return 0;}
- [51nod1383&1048]整数分解为2的幂
- [递推] 51Nod1383 整数分解为2的幂
- [51Nod 1048] 整数分解为2的幂 V2
- 整数分解为2的幂
- 1383 整数分解为2的幂
- 整数分解为2的幂
- 51nod 整数分解为2的幂
- 51nod-1383 整数分解为2的幂
- 【51Nod 1383】整数分解为2的幂
- 51nod 1383 整数分解为2的幂
- 【51NOD 1383】整数分解为2的幂
- [51Nod 1383] 整数分解为2的幂
- 51nod 1383 整数分解为2的幂
- 51Nod-1383-整数分解为2的幂
- [DP] 51Nod1048 整数分解为2的幂 V2
- 【51NOD 1048】【51NOD 1383】整数分解为2的幂 V2
- 51nod 1383&1048 整数分解为2的幂 [递推]【数学】
- [DP]51 Nod——[1048 整数分解为2的幂 V2]
- 基于FPGA的PCIe接口实现
- 第025 web服务器 apache服务器安装
- 完美无限滚动轮播图 +小圆点解决方案
- 第031讲 Apache环境配置暂时不看了直接PHPSTUDY自动配置
- 第035讲 php运行过程时序列
- [51nod1383&1048]整数分解为2的幂
- 认识和入门 WebRTC(Google Duo的一些启发)
- 第036+037讲 php变量和基本语法 及整形细节说明
- windows系统右键无法新建的问题
- 第038讲 布尔 浮点 字符串 运算符
- 多台客户端与服务器同时交互
- 海康相机接入流媒体实现实时转发,全天录像,历史回放,视频下载
- 第039讲 算数运算符 比较运算符 逻辑运算符
- UVa 607 - Scheduling Lectures