【二分|公式推导】2020.11.24 实验室测试(2020)F:老和尚的考核

Description
从前有座庙,庙里有个老和尚,老和尚的功夫高深莫测,有很多人都来到他的庙里,想要拜他为师,但是老和尚收徒的标准很高,他制定了多个考核项目,如果通过所有的考核内容,就可以正式的收他作为徒弟。
现在迎来了他的第一项考核项目:给你一个容量为V的大水桶,并给出你一个时间t(以天为单位),在规定的时间内要将该水桶灌满水。
前来挑战该项目的第一个人,他往水桶里灌水有一个规律,第一天只能灌入1体积的水,之后的每天灌入水的体积都比前一天多d体积。
如果他能顺利通过该考核,请输出他灌满该水桶用了多少天;如果不能通过该考核,请输出"What a pity!"(解释:输出包含引号)。

Input
输入数据有多组,第一行输入T,代表T组数据,接下来输入T组数据,每组数据占一行,依次输入三个整数V,t,d,代表的含义和题目描述一致。

Output
每组输入数据对应一行输出数据,如果能通过考核,输出灌满水桶的天数;如果不能通过考核,输出"What a pity!"。
Samples
Input

1
10 4 1

Output

4

Input

2
100 5 2
10 6 3

Output

"What a pity!"
3

在这里插入图片描述

这个题考察的就是等差数列的前 n n n 项和公式,为了提升一点难度,我就稍微加了一点点限制条件,嘿嘿,卡的就是暴力。
解题思路:
法一:利用前缀和公式结合图像分析判定根的情况来解决。
a n = a 1 + ( n − 1 ) ∗ d a_n=a_1+(n-1)*d an=a1+(n1)d
S n = n ∗ a 1 + n ∗ ( n − 1 ) ∗ d 2 ( n ∈ N ∗ ) S_n=n*a_1+\frac{n*(n-1)*d}{2}(n∈N^*) Sn=na1+2n(n1)d(nN)
S n = d 2 ∗ n 2 + ( a 1 − d 2 ) ∗ n S_n=\frac{d}{2}*n^2+(a_1-\frac{d}{2})*n Sn=2dn2+(a12dn
求 出 S n > = V 时 的 最 小 的 n , 当 然 这 个 n 肯 定 是 个 非 负 整 数 啦 求出S_n>=V时的最小的n,当然这个n肯定是个非负整数啦 Sn>=Vnn
移 项 得 S n − V > = 0 ; d 2 ∗ n 2 + ( a 1 − d 2 ) ∗ n − V > = 0 ; 移项得S_n-V>=0; \\\frac{d}{2}*n^2+(a_1-\frac{d}{2})*n-V>=0; SnV>=0;2dn2+(a12d)nV>=0;
上 述 公 式 中 a 1 与 d 都 是 已 知 条 件 上述公式中a1与d都是已知条件 a1d
在这里插入图片描述
无论对称轴在哪一侧,在y轴右侧一定存在一个根,因为所求的是非负整数,将 f ( n ) = 0 f(n)=0 f(n)=0的右侧的那个根上取整即为答案。

在代码中也有详细的解释,可参考理解一下。

上标程代码:

#include<stdio.h>
#include<math.h>
typedef long long ll;
int main()
{
	ll T,V,t,d;
	scanf("%lld",&T);
	while(T--)
	{
		/*等差数列前n项和公式 
		an=a1+(n-1)*d;
		Sn=(a1+an)*n/2; 
		Sn=n*a1+n*(n-1)*d/2;
		很明显,题意就是求Sn>=V时,n取得的最小值。 
		*/
		ll ans;
		scanf("%lld%lld%lld",&V,&t,&d);
		if(d == 0)
		{
			if(V > t)
			{
				printf("\"What a pity!\"\n");
			}
			else printf("%lld\n",V);
		}
		else{
			/*等差数列的前n项和就是一个二次函数,根据图像判断根的分布情况即可*/ 
			/*Sn=(d/2)*n^2+(a1-d/2)*n  ,使Sn >= V,则(d/2)*n^2+(a1-d/2)*n-V >= 0*/ 
			//二次函数的三个系数 
			double A = double(d * 1.0 / 2);
			double B = double(1 - d * 1.0 / 2);
			double C = double(-V);
			double delt = B * B - 4 * A * C;
			if(delt <= 0) ans = -B / 2 * A;
			else{
				/*二次函数为f(n)=A*n^2+B*n+C,是一个开口向上的二次函数*/ 
				//当n==0时,f(n) = C < 0;
				//所以一定有一个根小于0 ,只需判断右边那个根即可 
				double x_1 = (-B - sqrt(B * B - 4 * A * C)) / (2 * A);//可有可无 
				double x_2 = (-B + sqrt(B * B - 4 * A * C)) / (2 * A);
				
				ll n;
				n = ll(x_2);
				if(fabs(x_2 - n) < 1e-6) ans = n;
				else ans = n + 1; 
			}
			if(ans <= t)
			printf("%lld\n",ans);
			else printf("\"What a pity!\"\n");
		}
		
	}
	return 0;
}

时间复杂度: O ( n ) O(n) O(n)
注意:1、当公差为 d = 0 d=0 d=0时需要特别处理一下;2、输出“What a pity!”的时候注意要输出引号。

法二:二分
直接二分天数,判断在二分的那个天数后是否能将水桶灌满,寻找一个能灌满水桶且最小的天数。
直接搬来渣渣松同学的代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
    ll t;scanf("%lld",&t);
    while(t--){
        ll v,t,d;scanf("%lld%lld%lld",&v,&t,&d);
        ll sum=(t*d-d+2)*t/2;
        if(sum<v){
            printf("\"What a pity!\"\n");
            continue;
        }
        else{
            ll l=1,r=t;
            while(l<r){
                ll mid=l+r>>1;
                if(d*mid*mid+(2-d)*mid<2*v) l=mid+1;
                else r=mid;
            }
            printf("%lld\n",l);
        }
    }
    return 0;
}

时间复杂度: O ( n ∗ l o g ( n ) ) O(n*log(n)) O(nlog(n))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值