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!