【二分快速冪】病毒分裂

【問題描述】

  A學校的實驗室新研製出了一種十分厲害的病毒。由於這種病毒太難以人工製造了,所以專家們在一開始只做出了一個這樣的病毒。

  這個病毒被植入了特殊的微型芯片,使其可以具有一些可編程的特殊性能。最重要的一個性能就是,專家們可以自行設定病毒的分裂能力 K,假如現在有x 個病毒,下一個分裂週期將會有 Kx個一模一樣的病毒。你作爲該實驗室的數據分析員,需要統計出在分裂到第N個週期前,一共有多少個病毒單體進行了分裂。一開始時總是隻有一個病毒,這個局面算作第一個週期。由於答案可能很大,專家們只需要你告訴他們對給定的P取模後的答案。

【輸入格式】

  一行三個整數,依次是K, N, P。

【輸出格式】

  一行一個整數,你的答案(對P取模) 。

【輸入樣例】

【樣例1】
 5 3 7

【樣例2】
 2 6 23

【輸出樣例】

【樣例1】
 6

【樣例2】
 8

【樣例解釋】

  樣例一解釋:第一個週期有 1 個病毒,產生了一次分裂。第二個週期有 1*5=5 個病毒, 這五個病毒都會分裂。 所以第三個週期前一共進行了1+5等於 6 次分裂。 答案即爲6 mod 7 = 6。

【數據範圍】

1 < N < 10^18
1 < K , P < 2^31

一道考題
題目大意:有一個病毒,每次每個病毒都可以分裂成爲k個病毒,要求病毒在第n週期前總共分裂的次數。
顯然第一次只有一個病毒,在n=2時(也就是說在第二週期之前)只有一個病毒會分裂。次數爲1。
第二次有k個病毒,n=3時會分裂k次。總次數爲1+k。
第三次有k^2個病毒,n=4時會分裂k^2次。總次數爲1+k+k^2
……
歸納知第n-1次有k^(n-2)個病毒,n=n-1。總次數爲1+k+k^2+…+k^(n-2)
故本題答案Ans爲1+k+k^2+…+k^(n-2)。
顯然Ans爲等比數列{k^(n-2)}(n>=2)的各項和。由於本題規模大而且要取模,所以不能用通項公式,只能二分。
將1看作k^0,對和二分,設tmp=k^0+k^1+k^2+…+k^((n-2)/2),
則最後的和sum=tmp+tmp*a^((n-2)/2)-a^((n-2)/2)(n爲偶數)
或者sum=tmp+tmp*a^(n/2+1)(n爲奇數)
考試的時候莫名其妙爆棧了,結果一看是參數都是int,後面幾組都超了範圍,n也沒有用LL讀入,心塞啊。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstdlib>
using namespace std;
typedef long long LL;
LL k,n,p;
LL qkpow(LL a,LL b,LL p)//二進制法求k^n
{
    LL ans=1,t=a;
    while(b>0)
    {
        if(b&1)ans=(ans*t)%p;
        t=t*t%p;
        b=b>>1;
    }
    return ans;
}

LL solve(LL a,LL b,LL p)//二分求k^0+k+k^2+..+k^(n-2)
{
    if(b==0)return 1;//k^0=1

    LL tmp=solve(a,b/2,p);
    LL ans;

    if(b&1)ans=(tmp%p+tmp*qkpow(a,b/2+1,p)%p)%p;

    else ans=((tmp%p+tmp*qkpow(a,b/2,p)%p)%p-qkpow(a,b/2,p)%p+p)%p;//注意減法的模運算要加上模再取一次模 

    return ans;
}

int main()
{
    //freopen("virus.in","r",stdin);
    //freopen("virus.out","w",stdout);

    cin>>k>>n>>p;

    LL ans=solve(k,n-2,p);

    cout<<ans<<endl;

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章