P4862猜数(题解)
P4862猜数(题解)
PS.
讲一下个人对于这道题的心路历程吧。
这道题就是一道硬生生把两道题合并成一道的经典例子。
首先,看到这道题时,没有看到数据范围,然后这道题就被放置了很久。
后来才发现最后几个
然后,即使我就是菜
于是,我想骗一下
然后,过了很久,又想到去把
突然发现答案有关菲波那切数列,就做出来了。
借此我想告诉大家,我也不知道为什么答案是这样的
Solution.
70 point
首先,引进一个感性理解定理:
在m在S中并且S的元素数目不变的前提下,Iris选择的数m以及S的元素对答案是没有影响的。
所以,我们可以设
然鹅这个递推式的复杂度是
30 point
但是,我们把
我打出了这个表
1 ans=0
2 ans=2
3 ans=3
4 ans=4
5 ans=4
6 ans=5
7 ans=5
8 ans=5
9 ans=6
10 ans=6
11 ans=6
12 ans=6
13 ans=6
14 ans=7
经过整理后是这样的
1-1 0 1
2-2 2 2
3-3 3 3
4-5 4 5
6-8 5 8
9-13 6 13
. .
. .
. .
我们发现,最右端是斐波那契数列,然后再打一通表,就发现
所以复杂度是
至此,这道题终于做完了。
Coding.
有SB特判,有注释版本
#include<bits/stdc++.h> //我爱用万能头
using namespace std;
typedef long long ll;
const int N=2000,M=100; //N表示前7个点的范围,M表示后3个点的范围
int a,b,t;ll n,f[N+5];
inline void work1() //处理前7个点。
{
memset(f,0x3f,sizeof(f)),f[1]=0; //要求最大值的最小值,初始值INF
for(int i=1;i<=N;i++)
for(int j=1;j<i;j++)
f[i]=min(f[i],max(f[j]+a,f[i-j]+b)); //由上面的递推式得到
while(t--) scanf("%lld",&n),printf("%lld\n",f[n]); //回答询问
}
inline void work2() //处理后3个点。
{
f[0]=1,f[1]=1;
for(int i=2;i<=M;i++) f[i]=f[i-1]+f[i-2]; //预处理斐波那契数列
while(t--)
{
scanf("%lld",&n);
if(n==1) {puts("0");continue;} //SB特判
for(int i=1;i<=M;i++) if(n<=f[i]) {printf("%d\n",i);break;} //打表找出的规律
}
}
int main()
{
scanf("%d%d%d",&a,&b,&t);
if(a!=2||b!=1) work1(); //处理前7个点
else work2(); //处理后3个点
return 0;
}
不用SB特判,无注释版本:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2000,M=100;
int a,b,t;ll n,f[N+5];
inline void work1()
{
memset(f,0x3f,sizeof(f)),f[1]=0;
for(int i=1;i<=N;i++)
for(int j=1;j<i;j++)
f[i]=min(f[i],max(f[j]+a,f[i-j]+b));
while(t--) scanf("%lld",&n),printf("%lld\n",f[n]);
}
inline void work2()
{
f[0]=1,f[1]=1;
for(int i=2;i<=M;i++) f[i]=f[i-1]+f[i-2];
while(t--)
{
scanf("%lld",&n);
for(int i=0;i<=M;i++) if(n<=f[i]) {printf("%d\n",i);break;}
}
}
int main()
{
scanf("%d%d%d",&a,&b,&t);
if(a!=2||b!=1) work1();
else work2();
return 0;
}