专栏文章
B3930 [GESP202312 五级] 烹饪问题 题解
B3930题解参与者 6已保存评论 5
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 5 条
- 当前快照
- 1 份
- 快照标识符
- @miqmctiz
- 此快照首次捕获于
- 2025/12/04 07:09 3 个月前
- 此快照最后确认于
- 2025/12/04 07:09 3 个月前
看到各路大神都在用贪心,我就来发一个另类做法吧 (●'◡'●)。
题意描述
给定一个数列 ,求找到一组 ,使 最大。
约定记号
约定最优解的 取值为 , 取值为 。
即当 时, 最大。
即当 时, 最大。
解题思路
我们可以从高到低枚举每一位,逐步缩小寻找最优解的范围。
设当前寻找最优解的范围是 ,即目前可以钦定 。
初始时,。
初始时,。
先将所有数升序排序,考虑排序后这位上的情况,分讨一下。
- 这位全部相同
答案与这一位无关,可以直接跳过, 不用改动。

- 这位上有且仅有一个 ,位于数列末尾
则最优解的这位一定为 ,将末尾的数这一位置 ,重新排序。
因为只修改了末尾一个数,可以 插入排序,而 不用改动。

- 这一位上先是一段 ,然后是一段
先二分分界点,设首个这位为 的数是 。
可以确定 ,则令 ,缩小求解区间。

最后,当 范围缩小到两个数,即 ,则可以确定 ,求出最优解。
时间复杂度分析
首先需要对所有数排序,复杂度 。
后面缩小区间的操作最多进行 次,最坏每次插入排序有 的时间复杂度,则这一步复杂度为 ,常数有亿点点大。
所以总时间复杂度为 ,瓶颈在排序。
后面缩小区间的操作最多进行 次,最坏每次插入排序有 的时间复杂度,则这一步复杂度为 ,
所以总时间复杂度为 ,瓶颈在排序。
Code
注意代码下标是从 开始的。
CPP#include <cstdio>
#include <algorithm>
using namespace std;
char buf[1<<20],*p1,*p2;
int a[1000000];
inline char gc() // 快速读字符
{
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==buf?EOF:*p1++;
}
inline void read(int &x) // 快读
{
int c;
while((c=gc())<'0'||c>'9');
for(x=c^48;(c=gc())>='0'&&c<='9';x=(x<<3)+(x<<1)+(c^48));
}
int main()
{
int n,st,ed;
read(n);
st=0,ed=n-1; // 当前寻找最优解的范围
for(int i=0;i<n;++i)
{
read(a[i]);
}
sort(a,a+n);
for(int bit=1<<30;bit;bit>>=1) // 从高到低按位枚举,bit标识当前位
{
if((a[st]&bit)||!(a[ed]&bit)) // 这位上全部相同,跳过
{ // 即最小的数这位为1,或最大的数这位为0
continue;
}
if((a[ed]&bit)&&!(a[ed-1]&bit)) // 只有最后一个数这位为1
{
int t=a[ed]^=bit,i;
for(i=ed-1;i>=st&&a[i]>t;--i) // 单轮插入排序
{
a[i+1]=a[i];
}
a[i+1]=t;
}
else
{
int l=st,r=ed,mid;
while(l<r) // 二分0段和1段的分界点,找到第一个这位为1的数
{
mid=(l+r)>>1;
if(a[mid]&bit) r=mid;
else l=mid+1;
}
st=l; // 缩小范围
}
if(ed-st<=1) // 范围缩小到2个数以内,找到答案
{
break;
}
}
printf("%d",a[st]&a[ed]);
return 0;
}
完结撒花 ★,°:.☆( ̄▽ ̄)/$:.°★。
Update 2025.6.15:修正错误的图片。
Update 2025.6.16:微调公式。
Update 2025.6.16:微调公式。
相关推荐
评论
共 5 条评论,欢迎与作者交流。
正在加载评论...