[洛谷P1062/NOIP2006普及組] 數列(dp)
Accepted 100
用時: 27ms / 內存: 848KB
qwq我和你們這群用進制轉換的大佬完全不一樣qwq,昨天考了考這套卷子,(啥?進制轉換是啥?我怎麽不知道)....於是乎默默地敲了一發dp結果AC了qwq。
可能是這個題解區裏第一篇dp,,qwq,,本人也不是很厲害qwq有小錯誤請大佬們盡情PIP。廢話不說了開始吧qwq
首先題面是這樣的:
給定一個正整數 k(3≤k≤15) ,把所有k的方冪及所有有限個互不相等的k的方冪之和構成一個遞增的序列,例如,當 k=3 時,這個序列是:
1,3,4,9,10,12,13,…
因為所有的底數k都是相同的,所以自然要想到把他們的指數分離出來(這也是以後做題的時候要有的直覺qwq
例如這樣
然後把指數分離出來:
0,1,0+1,2,0+2,1+2,0+1+2,3....
這時候看可能沒什麽頭緒,但是再看一遍題目,你會發現題目中強調了兩個字qwq——————— 遞增。也就是說我們在確定第n項時,要從之前確定的n-1項中選出一項:
大於第n-1項但是小於目前能生成的任意一項,所以很容易想到:每確立一個數,就從數列的第一項開始逐個加上這一項,就造成了遞增的效果。
但是這樣做還有很大的缺陷,因為在前n-1項中,難免會有重復的項,舉個最簡單的例子:
0,1,0+1,2,0+2,1+2;
如果確立了第三項(0+1)的時候,對前面2項進行加法操作,明顯會造成重復,並且不符合題目要求(遞增和互不相等的方冪)。
那麽這個算法就要進行改進。
在這裏定義一下:
單獨數:就是不是由加法操作得到的數(k的n次方那種qwq)
合成數:由單獨數+合成數或由合成數+合成數組成的數
所以對於每一個合成數都有單獨數的參與,我們想,可不可以先預處理出k的1-n次方,顯然一個快速冪就可以了,那麽再想想,如果每讀入到一個單獨數,就可以用這個單獨數按照剛才的方式來得到後面的n-1項。
經過驗證顯然是可以的。
如樣例:k=3,n=100時:
用f[i]代表第i項,有:
令v=每一個單獨數f[i]
f[++i]=k(1 to n) v+f[k]
至此這個題目的分析就好了.....
下面是代碼qwq~
#include<bits/stdc++.h> #define re register #define ull unsigned long long using namespace std; int k,n,p; ull a[1000],f[2000000]; inline int read() //讀入優化 { int k=1; int sum=0; char c=getchar(); for(;‘0‘>c || c>‘9‘;c=getchar()) if(c == ‘-‘) k = -1; for(; ‘0‘ <= c && c <= ‘9‘; c = getchar()) sum = sum * 10 + c - ‘0‘; return sum * k; } inline void out(int x) //輸出優化 { if(x < 0) { putchar(‘-‘); x *= -1; } if(x > 9) out(x / 10); putchar(x % 10 + ‘0‘); } inline ull quick_pow(int r,int k) //快速冪 { ull base=r,ans=1; while(k!=0) { if(k&1) ans=ans*base; base=base*base; k/=2; } return ans; } int main() { //freopen("sequence.in","r",stdin); //freopen("sequence.out","w",stdout); k=read();n=read(); a[0]=1;a[1]=k; for(re int i=2;i<=n;i++) a[i]=quick_pow(k,i); //預處理k的1-n(保險) 次冪 for(re int i=1;i<=n;i++) { f[i]=a[p];p++; //對於每一個單獨數的賦值 ull tmp=f[i]; //記錄v值(單獨數) int h=i; //確立i-1項(避免後來i的更新) if(i>1) { for(re int j=1;j<h;j++) { f[++i]=tmp+f[j]; if(i>=n) { cout<<f[n]; //輸出 return 0; } } } } out(f[n]); return 0; }
好了,歡迎各位大佬來找本蒟蒻交朋友qwq
[洛谷P1062/NOIP2006普及組] 數列(dp)