[51nod1383&1048]整數分解為2的冪
題目大意
任何正整數都能分解成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段的和是
2 - 對於一個數
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的冪
題目大意 任何正整數都能分解成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
[51Nod 1048] 整數分解為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
51Nod 1383&1048 整數分解為2的冪
題目 任何正整數都能分解成2的冪,給定整數N,求N的此類劃分方法的數量。 V1:n≤106n≤106。要對答案模1e9+71e9+7。 V2:n≤1030n≤1030。將整個答案輸出。 解題
51nod 1383 整數分解為2的冪(數列,也可以自己根據觀察找規律推理得到遞推公式)
描述: 組合數學生成函式 1383 整數分解為2的冪 1 秒 131,072 KB 80 分 5 級題 任何正整數都能分解成2的冪,給定整數N,求N的此類劃分方法的數量!由於方案數量較大,輸出
51Nod-1383-整數分解為2的冪
ACM模版 描述 題解 看到這裡,我們應該可以想到,這是一個數論問題,應該是一個什麼數列,暴力解出來小資料後,在 OEIS 中查看了一下下,發現的確是一個十分有趣的數列——Binary partition function: number of p
【51NOD 1383】整數分解為2的冪
DH ---------以上初三THU/PKU大爺---- Alan_cty LYD XHM HZJ ZZ ---以下是大神%-- YMW Samjia2000 werkeytom_ftd Crazy_czy WorldWide_D Yxuan
51nod 1383 整數分解為2的冪(遞推)
Description 任何正整數都能分解成2的冪,給定整數N,求N的此類劃分方法的數量!由於方案數量較大,輸出Mod 1000000007的結果。 比如N = 7時,共有6種劃分方法。 7=1+1+1+1+1+1+1 =1+1+1+1+1+2
51nod 1383 整數分解為2的冪
#include<bits/stdc++.h> using namespace std; const int MAXN=1000100; const int mod=1e9+7; int
[51Nod 1383] 整數分解為2的冪
Description 任何正整數都能分解成2的冪,給定整數N,求N的此類劃分方法的數量!由於方案數量較大,輸出Mod 1000000007的結果。 比如N = 7時,共有6種劃分方法。
51nod-1383 整數分解為2的冪
原題連結 收藏 關注 任何正整數都能分解成2的冪,給定整數N,求N的此類劃分方法的數量!由於方案數量較大,輸出Mod 1000000007的結果。 比如N = 7時,共有6
[51nod1138]正整數分解為幾個連續自然數之和
sqrt esp 連續 奇數 mes 判斷 -i 兩個 註意 解題關鍵:註意為什麽上界是$\sqrt {2n} $ 因為函數是關於m的遞減函數,而結果必須為正整數 $a = \frac{{2n + m - {m^2}}}{{2m}} = \frac{n}{m} + \f
PTA7-37 整數分解為若干項之和(20 分)超級詳解
將一個正整數N分解成幾個正整數相加,可以有多種分解方法,例如7=6+1,7=5+2,7=5+1+1,…。程式設計求出正整數N的所有整數分解式子。輸入格式:每個輸入包含一個測試用例,即正整數N (0<N≤30)。輸出格式:按遞增順序輸出N的所有整數分解式子。遞增順序是指:
一個正整數分解為幾個連續的正整數之和
題目: 給定你一個數字 如:15 15可分解為 7+8 4+5+6 1+2+3+4+5 再如: 8 8不可分解為任何連續的正整數之和 所以輸出NONE 此題就是給定一個數字如果這個數字可以分解為
7-37 整數分解為若干項之和(20 分)
題目連結(組合版):點選開啟連結題目大意:略。解題思路:此方法僅限於輸出組合情況,計數的話會TLE。附加題目(計數版):點選開啟連結AC 程式碼(組合版)#include<bits/stdc++.
PTA 7-12 整數分解為若干項之和(20 分)
將一個正整數N分解成幾個正整數相加,可以有多種分解方法,例如7=6+1,7=5+2,7=5+1+1,…。程式設計求出正整數N的所有整數分解式子。 輸入格式: 每個輸入包含一個測試用例,即正整數N (0 < N ≤ 30)。 輸出格式: 按遞增順序輸出N的所有整數分解式
正整數分解為n個連續正整數
思路:等差數列求和: sn=a1*n+n*(n-1)*d/2 在這裡d為1 #include<stdio.h> //#define N 1000 #define M 10 void print(int k) {int sn=0,t=0;for(int a1=1;
7-1 整數分解為若干項之和(20 分)(dfs)
思路:不帶標記的dfs,只要沒有超過和就不斷dfs直到超過了之後向前回溯。 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<math
(PTA)7-1 整數分解為若干項之和
題目 將一個正整數N分解成幾個正整數相加,可以有多種分解方法,例如7=6+1,7=5+2,7=5+1+1,…。程式設計求出正整數N的所有整數分解式子。 輸入格式: 每個輸入包含一個測試用例,即正整數N (0 輸出格式: 按遞增順
pta 5-37 整數分解為若干項之和 (遞迴)
5-37 整數分解為若干項之和 (20分) 將一個正整數N分解成幾個正整數相加,可以有多種分解方法,例如7=6+1,7=5+2,7=5+1+1,…。程式設計求出正整數N的所有整數分解式子。 輸入格式: 每個輸入包含一個測試用例,即正整數N (0<<
面試總結:任意一個整數分解為幾個連續正整數之和
前陣子參加了國內某一大公司的面試。到了之後,人家不問出身,不問來歷,就直接開機讓我上機程式設計。因為是第一次在面試時上機操作,儘管題目不是很難,但是由於沒搞清楚機考和筆試的區別,導致最後面試失敗。現在總結一下自己在機考時碰到的一些問題,以免自己以後再犯同樣的錯