病毒分裂(分治)
题目:病毒分裂
|
【问题描述】 |
|
|
|
|
|
|
|
|
|
【输入格式】 |
|
|
|
|
|
|
|
|
|
【输出格式】 |
|
|
|
|
|
|
|
|
|
【输入样例】 |
|
|
|
|
|
|
|
|
|
【输出样例】 |
|
|
|
|
|
|
|
|
|
【样例解释】 |
|
|
|
|
|
|
|
|
|
【数据范围】 |
|
|
|
|
|
|
|
|
|
【来源】 |
|
|
|
|
题目分析:
对此类题目我们不妨现在草稿纸上先举例分析。
我们现在假设此刻我们的病毒一次能分裂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; }