2023“钉耙编程”联赛 Day 2 K 题 - SPY finding NPY

原题题面

题面由我校翻译组倾情翻译。

最近, SPY \text{SPY} SPY 已经退出了 XCPC \text{XCPC} XCPC(中国大学生编程竞赛)。但他零开始学习算法并赢得 ICPC \text{ICPC} ICPC(国际大学生编程竞赛)金牌的回忆仍然历历在目,所以他正在寻找一个青年作为他的学徒,取得金牌。 SPY \text{SPY} SPY 非常受欢迎,有 n n n 个青年想成为他的学徒。由于 SPY \text{SPY} SPY只需要一个学徒,所以他给这些青年设置了一个测试。

规则如下:

这些青年从 1 1 1 n n n 编号。 SPY \text{SPY} SPY 将按顺序面试这些青年,第 i i i 个青年将在第 i i i 个面试中接受测试。每个青年面试结束后, SPY \text{SPY} SPY 会得到他/她的智商指数( IQ \text{IQ} IQ,一个范围在 [ 0 , 202 3 202 3 2023 ] [0,2023^{2023^{2023}}] [0,202320232023] 的整数), SPY \text{SPY} SPY 可以决定是否接受他/她。一旦他接受了一个青年,测试就结束了,他不会再面试接下来的青年。一旦他拒绝了一个青年,他就不会再给他/她机会。

注意,没有两个青年具有相同的 IQ \text{IQ} IQ SPY \text{SPY} SPY 有一种特殊的策略来找到一个 IQ \text{IQ} IQ 高的青年。他在测试之前设置一个整数 k k k ( 0 ≤ k < n ) (0≤k<n) (0k<n)

1、无论前 k k k 个青年有多聪明,他们都会被拒绝。 SPY \text{SPY} SPY 会记录前 k k k 个青年中的最高 IQ \text{IQ} IQ 数值 x x x。如果 k = 0 k=0 k=0,则 x = − 1 x=−1 x=1

2、然后他将面试第 k + 1 k+1 k+1 n − 1 n-1 n1 个青年。一旦 SPY \text{SPY} SPY 面试到一个 IQ \text{IQ} IQ 高于 x \text{x} x 的青年,他会接受他/她并结束测试。

3、如果没有青年被接受, SPY \text{SPY} SPY 将接受第 n n n 个青年。

这些青年的 IQ \text{IQ} IQ 排名是随机的,也就是说他们的排名是 1 1 1 n n n 的一个排列,并且 n ! n! n! 种可能的情况发生的概率相等。尽管 SPY \text{SPY} SPY 是一个算法大师,但是他还是很难设定数字 k k k。请你帮助他设定一个最小化数字 k k k,使得他选择最大 IQ \text{IQ} IQ 青年的概率最大。

样例

输入样例:

8
1
2
3
4
9000
9001
9002
9003

输出样例:

0
0
1
1
3311
3311
3311
3312

思路解析

首先我们分析一手当有 n n n 个人的时候, k k k 对应的概率。

我们可以枚举他的位置为 i i i,对于这个位置,如果他被选中,我们显然应该让前 i − 1 i-1 i1 个数字的最大值选前 k k k 个,这样可以保证 SPY \text{SPY} SPY 不会在他之前选到 NPY \text{NPY} NPY

因为最大值不在前 k k k 个则会在 [ k + 1 , i − 1 ] [k+1,i-1] [k+1,i1] 这个区间被选中,只有最大值在前 k k k 项才能避免。

由于我们选择哪一个位置概率都一样,所以因此我们得到公式:
P = ∑ i = k + 1 n 1 n × k i − 1 = k n ∑ i = k + 1 n 1 i − 1 = k n ∑ i = k n − 1 1 i \begin{aligned} P&=\sum_{i=k+1}^n \frac 1 n \times \frac k {i-1}\\ &=\frac k n\sum_{i=k+1}^n \frac 1 {i-1}\\ &=\frac k n\sum_{i=k}^{n-1} \frac 1 {i} \end{aligned} P=i=k+1nn1×i1k=nki=k+1ni11=nki=kn1i1

由于前面一项不断增加,后面一项不断变小,不难发现图像长这样:
三分图像
因此我们发现这个函数有单峰性质,可以三分求极值。
时间复杂度 O ( log ⁡ 3 n ) O(\log_3n) O(log3n)

代码实现

由于一些精度上的问题,所以这里的三分应该是求出一个小区间,然后暴力在小区间找,时间复杂度不变。

#include<bits/stdc++.h>
#define LL long long
#define LD long double
using namespace std;
const LL N=1e4;
LL T,n;
LD sum[N+5];
LD cal(LL n,LL k)
{
	if(k==0)return 1.000/n;
	return 1.000*k/n*(sum[n-1]-sum[k-1]);
}
LL solve(LL n)
{
	LL l=0,r=n-1;
	while(l+10<r)
	{
		LL d=(r-l+1)/3;
		LL lmid=l+d,rmid=r-d;
		if(cal(n,lmid)<cal(n,rmid)) l=lmid;
		else r=rmid;
	}
	LL ans=-1;
	LD mx=-1;
	for(int i=l;i<=r;i++)
	{
		LD t=cal(n,i);
		if(mx<t)mx=t,ans=i;
	}
	return ans;
}
int main()
{
	for(int i=1;i<=N;i++)
	{
		sum[i]=sum[i-1]+(1.0000/i);
	}
	scanf("%lld",&T);
	while(T--)
	{
		scanf("%lld",&n);	
		printf("%lld\n",solve(n));
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值