题解 P2985 【[USACO10FEB]吃巧克力Chocolate Eating】
Yae_Sakura · · 题解
吐槽:这题难度应该是黄色吧
本题最大的坑点是记录食用日期 ( check函数不会太难写的 )
我个人的想法是先后求解两个子问题
- 先二分求出最不开心那天的最大开心值ans
- 再对ans重新check一遍以记录正确日期
原因:借鉴楼上:“因为这个程序会不管三七二十一地把所有的日期记录下来,不管可行与否”,所以只有当求出最优解ans后,check记录的日期才会是正解
#include<cstdio>
#define ll long long
//N<=50000 H_i<=1000000 累加起来将超过int范围
using namespace std;
const int N=50005;
inline ll read()
{
ll s=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*f;
}
ll l,r,mid,ans;
ll n,d,a[N],day[N];
inline bool check(ll x)
{
ll tot=0,sum=0;
for(int i=1;i<=d;i++){
sum=sum>>1;
//">>1"表示除以2的一次方
while(sum<x){
sum+=a[++tot];
if(tot>n) return false;
//达不到检查值x便返回
if(x&&x==ans) day[tot]=i;
//if保证仅对ans记录日期
//(当然可以去掉if,不过反复记录非正解很浪费)
}
}
return true;
}
int main()
{
n=read();d=read();
for(int i=1;i<=n;i++)
a[i]=read(),r+=a[i];
while(l<=r){
mid=(l+r)>>1;
if(check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
//二分最低开心值的区间
printf("%lld\n",ans);
check(ans);
for(int i=1;i<=n;i++){
if(day[i]) printf("%lld\n",day[i]);
else printf("%lld\n",d);
//最后一个小坑点:在最后一天要吃完剩下的全部巧克力
//“剩下”即未标记过食用日期的巧克力
}
return 0;
}
评测结果:AC 68ms(不上不下的)