申請SAE

如果您發現本博客的外觀很難看,那是因為部分外觀文件被中國.國家.防火.牆屏.蔽所致!
請翻~牆!

我的Wordpress博客的地址: http://zhuyf.tk/

2011年11月8日 星期二

[數值遞推]NOIP模擬題 :產生01串 infinit 解題報告

【問題描述】
我們按以下方式產生序列:
1、 開始時序列是: " 1 " ;
2、 每一次變化把序列中的 " 1 " 變成 " 10 " ," 0 " 變成 " 1 "。
經過無限次變化,我們得到序列" 1011010110110101101... "。
總共有 Q 個詢問,每次詢問爲:在區間A和B之間有多少個1。
任務 寫一個程序回答 Q個詢問
輸入 第一行爲一個整數 Q,後面有Q行,每行兩個數用空格隔開的整數 a , b 。
輸出 共 Q行,每行一個回答
約定
1 <= Q <= 5000
1 <= a <= b < 2^63
樣例

infinit.in
infinit.out
1
2 8
4

【分析】
S1 = "1"
S2 = "10"
S3 = "101"
S4 = "10110"
S5 = "10110101"

Si 是 S(i+1)的前綴。
序列Si 是由序列 S(i-1)和S(i-2), 連接而成的。
即Si = S(i-1)+S(i-2) (實際上是Fibonacci數列)。

找到規律以後,可以先求出從位置1到位置X之間所有的1的個數。
用一個函數F計算,結果爲f(b)-f(a-1)。

每一項1的個數以及每一項的長度都符合數列規律.長度的第93項恰好大於2^63,所以遞推到92項即可  

 【我的代碼】
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
unsigned long long F[95];
unsigned long long L[95];

unsigned long long Getm(unsigned long long  x)
{
    int i1;
    unsigned long long getm=0;
    while(x!=0)
    {
        for(i1=1;i1<=92;i1++)
        {
            if(L[i1]>x)
            {
                getm+=F[i1-1];
                x-=L[i1-1];
                break;
            }
        }
    }
    return getm;
}

int main()
{
    freopen("infinit.in","r",stdin);
    freopen("infinit.out","w",stdout);
   
    F[0]=0;
    F[1]=1,L[1]=1;
    F[2]=1,L[2]=2;
    for (int i=3;i<=92;i++)
    {
        F[i]=F[i-1]+F[i-2];
        L[i]=L[i-1]+L[i-2];
    }
    int N;
    scanf("%d\n",&N);
    for (int i=1;i<=N;i++)
    {
        unsigned long long a,b;
        cin>>a>>b;
        a--;
        cout<<Getm(b)-Getm(a)<<endl;
    }
    return 0;
}
 

沒有留言:

張貼留言