病毒分裂(分治)

题目:病毒分裂

 

【问题描述】

 

 


  A学校的实验室新研制出了一种十分厉害的病毒。由于这种病毒太难以人工制造了,所以专家们在一开始只做出了一个这样的病毒。

  这个病毒被植入了特殊的微型芯片,使其可以具有一些可编程的特殊性能。最重要的一个性能就是,专家们可以自行设定病毒的分裂能力 K,假如现在有x 个病毒,下一个分裂周期将会有 Kx个一模一样的病毒。你作为该实验室的数据分析员,需要统计出在分裂到第N个周期前,一共有多少个病毒单体进行了分裂(注意求的不是最终的细胞个数。。。LZ就这么错的0.0。一开始时总是只有一个病毒,这个局面算作第一个周期。由于答案可能很大,专家们只需要你告诉他们对给定的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

 

 

 

 

 

【来源】

 

 


八中命题。

 

题目分析:

对此类题目我们不妨现在草稿纸上先举例分析。

我们现在假设此刻我们的病毒一次能分裂3倍(怠惰啊!),而我们要计算的是4周之内分裂的次数(注意是分裂次数,而不是最终的分裂个数。。。LZ就这么错的诶)。

根据我们的例子很容易便能推导出:第n周内进行分裂的细胞个数就是 1+n+n*n+n^3+n^4+......+n^(n-2)

那么到这里各位已经做完这道题的大半了,剩下的便是针对上式的分析了

 

方法1暴力求解

暴力枚举,老老实实地计算每一项的幂,再进行相加。当然,最简单的方法亦只能得到30分。(请仔细看数据范围)

 

方法2分治求解!

提到分治首先我们要想到的便是二分(是LZ见识太少了么.....)

上式我们可以分成两个板块:求K的幂和求K^0 => k^(n-2) 的和(求幂的和)

请直接转至代码处观看详细讲解

 

 

技巧:

有人问了,既然暴力求解法得不到满分,那么LZ将它列处来有什么意义呢?很简单呀,它简单小巧又好写,在我们想不到其他方法的时候不妨现将它写粗来保证30分。好当然我绝对没有嘲笑XX  IQ的意思,那么对于想到分治方法的同学来说它有什么用处呢,那么我们就要提到一个竞赛技巧了: 对拍   

对于一道题来说我们总是能想到2种或2种以上的方法,那么除了满分方法意外,另外一种或几种方法便能与满分方法进行对拍。众所周知,考试种方法很容易便能想出来,但更难的是如何去将方法的代码无误的实现出来。所以对于这种题我们错的往往不在于超时,而在于代码出错。而相反,暴力方法虽然会超时,但代码出错基本是不会有的(PS:被自己打脸。。。),这时候我们便可以用暴力程序来检验我们的满分程序,从而找到错误。

 

好,水了这么多段落,我们来看一看如何更简单得实现对拍。

首先我们满分程序输入文件名称为:virus.in 输出文件名称为:virus.out

那么我们对拍程序输入文件名同为:virus.in 但输出文件名为:virus_dp.out

 

同时我们需要一个数据生成程序(参考代码处) virus_data.exe(可用C++编译)

 

以及一个进行比较输出数据的小程序(系统自带:fc      可直接搜索,一般在C盘内)

 

那么我们新建一个bat文件,并在其中编辑:

@echo off         清除冗杂指令,诸位可以试试不加这句的后果(密恐请勿尝试)

:loop             跟后面Onto loop组成循环

  virus_data.exe   运行数据生成程序

  Virus.exe        运行满分程序

  virus_dp.exe     运行对拍程序

  fc  virus.out  virus_dp.out   比较两者的输出数据

  If  not  errorlevel  1  onto loop    如果未出错便比较下一组数据

  Pause           如果有错便暂停

onto loop          比较下一组数据

 

代码处:

1.满分代码

 

#include<iostream>
#include<fstream>
#include<cstdio> #define ll long long using namespace std; ll k,n,p; ll qkpow(ll a,ll b){ //二分求幂 if(b==0) return 1; //给出边界:当计算a^0返回答案1 ll m=b/2; //二分指数 ll cmt=(qkpow(a,m))%p;//先求出a^m的值 ll s=(cmt*cmt)%p; //通过a^n=a^m*a^m 求出a^n if(b&1) s=(s*a)%p; //如果n是奇数的话应该再乘上一个a return s; } ll solve(ll a,ll b){ //二分求幂和 a^1+a^2+....+a^b if(b==1) return a; //给出边界:当计算a^1时返回答案a ll m=b/2; ll cmt=solve(a,m); //求出 a^1+a^2+....+a^m=cmp ll t=qkpow(a,m); ll s=(cmt*t+cmt)%p; //通过a^1+a^2+...a^n=cmp*a^m+cmp if(b&1) s=(s+qkpow(a,b))%p;//同样要特殊对待奇数的b return s; } int main(){
freopen("virus.in","r",stdin);
freopen("virus.out","w",stdout); ll na; cin
>>k>>n>>p; ll ans=(solve(k,n-2)+1)%p; cout<<ans; return 0; }

 

2.数据生成器

#include<cstdio>
#include<cstdlib>
#include<ctime>
using namespace std;


int main()
{
   freopen("virus.in","w",stdout);
   srand(time(NULL));
   int n,k,p;
   
   n=rand()+2;
   k=rand()+2;
   p=rand()+2;

   printf("%d %d %d\n",k,n,p);    
   return 0;    
}

 

posted @ 2016-08-02 17:00  Wannabtl  阅读(1615)  评论(0编辑  收藏  举报