NOIP2025 T1 三分+二分做法

揭秘一个神秘的做法,忽略读入和排序之后仅需要 \(\mathcal O(\log n\log m)\) 的时间复杂度,目前没有看到比这个做法更优的。

题意回顾

一家糖果店卖 \(n\) 种糖果,每种糖果都有无穷多颗。第 \(i\) 颗糖果在第奇数次被卖出时耗费 \(x_i\) 元,在第偶数次卖出时耗费 \(y_i\) 元。手中总共持有 \(m\) 元,问最多能买多少颗糖果。

数据范围:\(1\leq n\leq 10^5,1\leq m\leq 10^{18},\forall i\in[1,n],1\leq x_i,y_i\leq 10^9\)

题解

首先给出一个结论:一种糖果会被多次购买,当且仅当它是满足 \((x_i+y_i)\) 最小的那一种糖果。

证明:如果是其它糖果买了多次,调整为多次买 \((x_i+y_i)\) 最小的那一种糖果是绝对有利的,因为相同的数量花更少的价格,且没有任何副作用。

所以购买数量的构成就是:\(x_i+y_i\) 最小的那种糖果买了很多,假设买了 \(k\) 轮,其中一轮表示买两次。剩下一种买了一颗。

正常人的思路都是直接去枚举买一颗的那些是买了多少种,而我在这里开始产生分叉。以下把 \((x_i+y_i)\) 最小的那种糖果称为“特殊糖果”。

不妨设 \(\min_{i=1}^{n}\{x_i+y_i\}=z\)。考虑刻画关于 \(k\) 的函数 \(f(k)\) 表示买了 \(k\) 特殊糖果后,最多能买几个糖。那么有如下表达式:

\[f(k)=2k+(用 (m-kz) 元最多能买几个单块糖果) \]

接下来就需要研究函数性质了。我在考场上通过打表得出了这个结论:\(f(k)\) 是一个单峰函数。

接下来给出证明。

\(g(r)\) 表示用 \(\leq r\) 元的钱最多能买多少个单块糖果。容易知道 \(g(r)\) 是单调不降的。而且函数图像是上凸趋势的。

那么有

\[f(k)=2k+g(m-kz) \]

考虑 \(f(k)\) 的一阶差分 \(d(i)\)

\[d(i)=f(i+1)-f(i) =2(i+1)+g(m-(i+1)z)-2i-g(m-iz) \]

\[d(i)=2-[g(m-iz)-g(m-(i+1)z)] \]

\(2\) 后面这一项 \(g\) 的差值意味着前面多花 \(z\) 元买,能多卖多少。随着 \(i\) 的不断增大,\(m-iz\) 不断变小。那么前后两项差值反而是不会变小的。

由此,\(d(i)\) 单调不升,\(f(x)\) 单峰得证。

那么主函数里使用三分找峰,函数值用到二分查找进行计算。这题就轻松跑过去了。

当然不可避免地需要排序。所以总复杂度 \(\mathcal O(n\log n)\)。但答案统计方面做到了 \(\mathcal O(\log n\log m)\)。感觉快顶到理论最优了?

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
int n,m;
struct node
{
	int x,y;
}a[N];
int mn=1e18;
int s[N];
bool cmp(node r1,node r2)
{
	return r1.x<r2.x;	
}
int f(int x)
{
	int cnt=x*2;
	cnt+=upper_bound(s+1,s+1+n,m-x*mn)-s-1;
	return cnt;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	// freopen("candy.in","r",stdin);
	// freopen("candy.out","w",stdout);
	cin>>n>>m;
	for(int i=1;i<=n;++i)
	{
		cin>>a[i].x>>a[i].y;
		mn=min(mn,a[i].x+a[i].y);
	}
	sort(a+1,a+1+n,cmp);
	int ans=0;
	for(int i=1;i<=n;++i)
	{
		s[i]=s[i-1]+a[i].x;
	}
	int L=0;
	int R=m/mn;
	while(L<=R)
	{
		int mid1=(R+2*L)/3;
		int mid2=(2*R+L)/3;
		if(f(mid1)<=f(mid2))
		{
			L=mid1+1;
			ans=max(ans,f(mid1));
		}
		else
		{
			R=mid2-1;
		}
	}
	cout<<ans<<"\n";
	return 0;
}
posted @ 2026-03-01 03:10  Dexember  阅读(2)  评论(0)    收藏  举报