2018.9.7南海中学测试T1(等比数列二分求和)

A病毒分裂
Time Limit:10000MS Memory Limit:165536K
Case Time Limit:1000MS
Description
A 学校的实验室新研制出了一种十分厉害的病毒。由于这种病毒太难以人工制造了,所以专家们在一开始只做出了一个这样的病毒。
这个病毒被植入了特殊的微型芯片,使其可以具有一些可编程的特殊性能。最重要的一个性能就是,专家们可以自行设定病毒的分裂能力K,假如现在有x 个病毒,下一个分裂周期将会有Kx 个一模一样的病毒。你作为该实验室的数据分析员,需要统计出在分裂到第N 个周期前,一共有多少个病毒单体进行了分裂。一开始时总是只有一个病毒,这个局面算作第一个周期。由于答案可能很大,专家们只需要你告诉他们对给定的P 取模后的答案。
Input
一行三个整数,依次是K, N, P。
Output
一行一个整数,你的答案(对P 取模) 。
Sample Input
【样例输入1】
5 3 7
【样例输入2】
2 6 23
Sample Output
【样例输出1】
6
【样例输出2】
8

这道题目,模拟一下就会发现,就是等比数列求和。首先想到要用的东西就是快速幂,毕竟可以使用快速幂来暴力。

所以一开始我先打了一个O(n)的暴力算法:

#include<iostream>
#include<stdio.h>
using namespace std;
long long k,n,p,d=1,and_;
void read();
int main()
{
    read();
    for(long long i=1;i<n;i++)
    {
        and_+=d;
        and_%=p;
        d*=k;
        d%=p;
    }
    if(and_==0)
      and_=1;
    printf("%lld",and_%p);
    return 0;
}
void read()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%lld%lld%lld",&k,&n,&p);
    return ;
}

然后,不知不觉,就AC了!
数据特别水,所以就AC了。然后很多人说要用逆元,他们做错了,所以就特别不爽我们这些用O(n)拿满分的人;
然后,我们可敬的cmb指导老师就说出了好多句金句:
“呵呵,noip多次证明水分的重要性”
“事实证明不屑水分的人都被分数所不屑”
“所以,认真贯彻比赛纪律,水分对拍正解。”

所以听从老师的指导,以后我都水分出来先hhhh。


然而一直水分也不是好的,现在就讲讲这道题目的正解吧。

这道题用了一个很厉害的一个公式: 等比数列二分求和

当n==0时,S=1;

当n%2==0时,也就是n为偶数时:
这里写图片描述

当n%2==1时,也就是n为奇数时:
这里写图片描述

其实,仔细认真推一下,是可以推出来的,贴上我的代码:

long long solve(long long k,long long n)
{
    if(n==1)return k%p;
    int t=solve(k,n/2);
    if(n&1)
    {
        long long t1=doit(k,n/2),t2=doit(k,n);
        t=(t+t1*t%p+t2)%p;
    }
    else
    {
        long long t1=doit(k,n/2);
        t=(t+t1*t%p)%p;
    }
    return t%p;
}

然后就是这道题目,只要仔细认真读好题目,知道求的是什么,那么稍作修改,就可以AC了。

贴上代码:

#include<iostream>
#include<stdio.h>
using namespace std;
long long k,n,p,d,S;
void read();
long long doit(long long k,long long n) {
    if(n==1)  return k%p;
    long long t=doit(k,n/2);
    long long ans=t*t%p;
    if(n%2==1)  ans=ans*k%p;
    return ans;
}
long long solve(long long k,long long n)
{
    if(n==1)return k%p;
    int t=solve(k,n/2);
    if(n&1)
    {
        long long t1=doit(k,n/2),t2=doit(k,n);
        t=(t+t1*t%p+t2)%p;
    }
    else
    {
        long long t1=doit(k,n/2);
        t=(t+t1*t%p)%p;
    }
    return t%p;
}
int main()
{
    read();
    S=solve(k,n-2)+1;
    printf("%I64d",S%p);
    return 0;
}
void read()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%I64d%I64d%I64d",&k,&n,&p);
    return ;
}

最后,注意一点:long long! long long! long long!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值