猴子吃香蕉问题
题目描述
链接:https://ac.nowcoder.com/acm/problem/14392
来源:牛客网
有n只猴子,第i只猴子每过xi小时会连续吃香蕉yi小时。猴子从第二次开始每次休息结束后这只猴子连续吃香蕉的时间会增加zi小时。
给定n只猴子,每一只的xi,yi,zi,以及时间t,求在前t小时中,所有猴子共吃了多少小时。
对于一只猴子来说是这样的:
从第1小时开始:
休息xi小时( 1 -> xi )
吃yi小时( xi + 1 -> xi + yi )
休息xi小时
吃yi+zi小时
休息xi小时
吃yi+zi+zi小时
......
输入描述:
第一行两个数n和t;
之后n行,第i+1行每行三个数xi,yi,zi.
输出描述:
一行一个数表示答案.
输入
10 100000000
1 0 0
1 0 5
1 2 2
1 2 8
1 3 0
1 5 0
1 5 2
1 5 5
1 7 0
1 8 3
输出
845787522
输入
1 233333
233 233 233
输出
223081
这道题的思路刚开始是暴力搜索,但是会超时
仔细分析发现本质是等差数列求和
#include<iostream>//猴子吃香蕉题解版
using namespace std;
#define maxn 100000
typedef long long ll; //这道题我通过暴力搜索超时了,最后发现这道题本质是等差数列求和
int main()
{
int N, T; //由题意可得猴子的数量为N(至于为什么是大N,是为了和下放的小n区分)每只猴子休息加吃香蕉的总时间为T
cin >> N >> T; //输入N,和T
ll* x = new ll[maxn]; //在栈区开辟大数组最大只能开辟2M的空间,100000个整型数组开辟的空间大于2M,估使用new开辟在堆区
ll* y = new ll[maxn];
ll* z = new ll[maxn];
ll sum; //sum用来统计所有猴子吃香蕉的时间
for (long long i = 0; i < N; i++) cin >> x[i] >> y[i] >> z[i];//使用三个数组分别对应储存x,y,z
for (long long i = 0; i < N; i++)
{
if (z[i] == 0 && y[i] != 0) //这次处理第一次特殊情况,就是当等差数列d=0的情况
{
sum += T / (x[i] + y[i]) * y[i];
if (T % (x[i] + y[i]) - x[i] > 0)
sum += T % (x[i] + y[i]) - x[i];
continue;
}
//二分法搜索
//这道题一旦明白等差数列的本质,那么我们唯一的难点就是找等差数列中的n,我们通过二分法找从而降低时间复杂度
long long left = 0, right = 100000, mid, n, m;
while (left <= right)
{
mid = (left + right) / 2; //mid可以看成n,我们通过二分法来找n
if (mid * (x[i] + y[i]) + mid * (mid - 1) / 2 * z[i] <= T)//等差数列前n项和 sn=n*a1+n(n-1)/2*d,n=mid,a1=(x[i]+y[i]),d=z[i]
left = mid + 1, n = mid; //当满足条件就讲mid的值赋于n,我们就通过二分法找到了n
else right = mid - 1;
}
sum += n * y[i] + n * (n - 1) / 2 * z[i]; //这里就是简单的等差数列求和,不过注意,这时候的a1=Y[i]
m = T - n * (x[i] + y[i]) - n * (n - 1) / 2 * z[i];
m -= x[i]; //我们还需要考虑一种情况,就是在当n*(x[i]+y[i])!=T
if (m > 0 && y[i] && z[i]) sum += m; //这时候我们需要考虑是否满足刚好休息或者休息完刚好吃香蕉直到时间大于T
else if (m > 0 && !y[i] && z[i]) sum += m; //我们使用if判断来处理这种异常情况
}
cout << sum << endl;
delete[]x; //开辟在堆区的数组记得手动释放
delete[]y;
delete[]z;
}