1. 程式人生 > >[洛谷P1062/NOIP2006普及組] 數列(dp)

[洛谷P1062/NOIP2006普及組] 數列(dp)

pip pow names 發現 遞增 技術分享 重復 alt 明顯

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)