Cpp环境【Vijos3160】【NOIP Practice】病毒分裂

【问题描述】 

  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

【来源】    

  重庆八中命题。

【思路梳理】

  题不难,模拟考试时非常紧张就WA掉了。大致思路还是比较清晰:一个二分快速幂,再来一个二分求幂和两个分治算法。设d[i]=在第i个周期前(注意是在第i个周期以前,即还没有进行第i次分裂)病毒分裂的次数。因为第一个周期有且仅有一个病毒,所以显然d[2]=1=k^0,d[3]=k=k^1,d[4]=k*k=k^2,etc,不难发现对于我们所求的结果sum[n]有:
      sum[n]=d[2]+d[3]+…+d[n-2]
          =k^0+k^1+k^2+…+k^n-2。
  公式出来了就不难实现了。每一次进行求和时进行二分:

所求的部分的和S = a^1 + a^2 + … + a^n
设前半部分的和Lhalf = a^1 + a^2 + … + a^(n/2)
显然后半部分的和Rhalf=a^(n/2+1)+a^(n/2+2)+ … + a^(n)
           =Lhalf×a^(p/2)

则当n为偶数时: S=Lhalf + a^(n/2)*Lhalf
则当n为奇数时: S=Lhalf + a^(n/2)*Lhaf + a^n

  因为幂次可能很大,所以使用二分快速幂不再赘述,直接给出Cpp代码。

【Cpp代码】
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
long long n,k,m,ans=0;

long long mod(long long x)
{
    return x%m;
}

long long qkpower(long long x,long long p)//二分快速幂
{
    if(p==0)    return 1;

    long long t=mod(qkpower(x,p/2));
    long long cnt=mod(t*t);
    if(p%2) cnt=mod(cnt*x);
    return cnt;
}

//ans=1+(k^1+k^2+...+k^n-2)
long long solve(long long x,long long p)//solve每一次计算的是从k开始的和
{
    if(p==0)    return 0;

    long long t=solve(x,p/2);
    long long cnt=mod(t+t*qkpower(x,p/2));
    if(p%2) cnt=mod(cnt+qkpower(x,p));
    return mod(cnt);    
}

int main()
{
//  freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);
    cin>>k>>n>>m;
    ans=solve(k,n-2);
    cout<<mod(ans+1);//k^0实际上是没有加到的
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值