前言
差点一套把我直接送走
60+0+0+40=100…
Problem 1 数列问题
题目描述
小明和小红在一起玩游戏,小明觉得现在玩的游戏太无聊了,便给小红出了一道数列问题。数列问题的题目如下:
一开始,小明给小红一个只含有数字1的数列,接下来,小红可以对这个数列进行以下操作中的一种操作:
· 对序列中已有的一个元素加1.
· 复制一个序列中已有的元素到序列的末尾.
比如:一开始序列中只有一个元素[1]
我们可以选择这个序列中的第一个元素进行第二种操作,即复制操作,这样,这个序列就变成了[1,1]
然后,我们可以对这个序列中的第一个元素进行第一种操作,即将第一个元素进行+1.这样,这个序列就变成了[2,1]
现在,小明问小红,她最少需要进行多少次操作,可以使得序列中所有元素的和至少为n。
小红面对小明的这个问题一脸懵逼,于是,她来求助聪明的你,希望你能够帮助她解决这个问题。
输入格式
第一行一个数字t,表示测试数据的组数。
接下来行,每行一个数字,表示序列所有元素和至少应该达到的数。
输出格式
对于每一个问题,回答一个数字,表示小红最少需要操作的次数。
样例
样例输入:
5
1
5
42
1337
1000000000
样例输出:
0
3
11
72
63244
手推数据,发现规律为:0,1,2,2,3,3,4,4,4,5,5,5,6,6,6,6,7,7,7,7…
Solution1:map暴力枚举处理每个循环节开始的数,再在查询时找1~1e9中的
数,第一个大于等于它的数的操纵数即为答案,O(1e9t),再用一个map映射原数的话O(63244t) TLE 60
Solution2:公式,我不会证,但能想到成次方增加最优
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
int t,n;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
int k=0;
for(int i=1;i<=n;i++)
{
if(i*i>=n)
{
k=i;
break;
}
}
if(k*(k-1)>=n)
{
printf("%d\n",k*2-3);
}
else{
printf("%d\n",2*k-2);
}
}
return 0;
}
手推公式的题还是第一次考啊。。。
Problem 2 Teamwork
传送门
考试时没想到怎么处理两个组之间的数的所属,打了个假贪心,WA 0
Solution:设d[i]表示到i时能获得的最大价值,因为它所属的组最多从max(0,i-k+1)开始,min(n,i+k-1)结束,与其他元素无关,又因为i+k-1会被后面的数的前一部分统计到,所以只考虑前一部分
则dp[i]=max(dp[i],dp[j]+maxn*(i-j))
其中 (上一组最后一个)i-k<=j<=i-1
maxn表示组内最大值,用RMQ优化
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
int maxn,n,k,a[10005],dp[10005],f[100005][20];
void ST_chushi()
{
for(int i=1;i<=n;i++)
{
f[i][0]=a[i];
}
int t=(int)(log(n)/log(2))+1;
for(int j=1;j<t;j++)
{
for(int i=1;i<=n-(1<<j)+1;i++)
{
f[i][j