hdu 2862 The Crazy O2jamer 二分暴力模拟
题意:COOL:200分/个,GOOD:100分/个
JAMS:得到一个COOL可以得到1/25 JAM,得到一个GOOD可以得到1/50 JAM
得到了一个或以上的JAM,每个JAM会加额外的10分给每个COOL、5分给每个GOOD
COMBO:连续按x个:x COMBO,失误,则COMBO归零,且所有的JAM会失去
比如,得到了45个COOL,65个GOOD,1个MISS,38个COOL,分数为\(200*25+210*20+105*10+110*50+115*5+200*25+210*13=24055\)
想要得到至少s分,总共n个按键,最大的COMBO数尽量小,一把游戏最多拿c个COOL,GOOD和MISS随意
如何得到最少的MAX COMBO,输出这个数量
题解:二分+模拟
显然可以二分,因为combo大分数肯定也多
我想了这题足足有一天,为此还写了一大篇markdown,真的服了,网上找不到大佬的提交代码,其实后来发现可以上vj上找,只要有人share了就可以看到他们的ac代码。
当我看到直接O(n)的check我就当场Orz。。。泪奔……
我还妄想用一种O(1)的check……但是事实上太麻烦了……对拍了半天才偶尔出一组不一样的数据……实在调不出来了
O(1)的代码目前还没通过,哪位如果尝试用O(1)check过了欢迎评论区交流……等会我会放出我那篇超长篇的md供大家批评指正
下面是O(nlogn)的二分暴力代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=5e4+10;
int n,s,c;
int node[N];
bool check(int cir)
{
int tot=c;
for(int i=1,cnt=0;i<=n;i++)
{
if(tot&&cnt<cir)
node[i]=2,cnt++,tot--;
else if(cnt<cir)
node[i]=1,cnt++;
else node[i]=cnt=0;
}
int score=0,jam=0;
for(int i=1;i<=n;i++)
{
if(node[i])
{
score+=node[i]*100+jam/50*node[i]*5;
jam+=node[i];
if(score>=s)return 1;
}
else jam=0;
}
if(score>=s)return 1;
else return 0;
}
int main()
{
while(~scanf("%d%d%d",&n,&c,&s))
{
int l=0,r=n,ret=-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) r=mid-1,ret=mid;
else l=mid+1;
}
if(ret==-1)puts("impossible");
else printf("%d\n",ret);
}
return 0;
}
下面是想了超久的O(1)但代码没过,先放出来以后再看吧
maxcombo: mid
1~mid :
all cool : mid, floor(mid/25) 次 JAM,
[1-25] 26-50 51-75 76-100 ... [floor(mid/25)*25+1 - mid]
1 2 3 4 (floor(mid/25)+1)段
如果mid%25==0,只有floor(mid/25)段
\(200*25 + (200+10)*25 + (200+10*2)*25 +... +(200+10*(floor(mid/25)-1))*25\)
如果mid%25!=0,有floor(mid/25)段是完整的,最后一段长度为\(res=(mid-(floor(mid/25)*25))=mid\%25\)
在以上基础上加入\((200+10*(floor(mid/25))*res\)
all good 同理
函数:cal(ori,cir,bonus,mid) 起始得分ori,循环节cir,加成bonus,在连续的mid次按键中,加成为
\(\sum_{i=0}^{i=floor(mid/cir)-1} (ori+bonus*i)*cir+(ori+bonus*floor(mid/cir))*res)\)
\(ori*cir*floor(mid/cir)+bonus*cir\sum_{i=0}^{i=floor(mid/cir)-1}i+(ori+bonus*floor(mid/cir))*res)\)
\(ori*cir*floor(mid/cir)+bonus*cir*(floor(mid/cir)-1)*floor(mid/cir)/2+(ori+bonus*floor(mid/cir))*res)\)
在n次按键中,要分成长为mid+1的若干段,前面可以分成的完整段数为cnt=\(floor(n/(mid+1))\),最后一段可能剩下的长度为\(n\%(mid+1)\)
1.若n%(mid+1)==0,则分成总共cnt段
最多拿c个cool,且拿不满,那么就是要c个cool尽量排在前面。
拿满的情况贡献为\(cal(200,25,10,mid)*floor(n/(mid+1))\)
先计算前floor(c/mid)段的完整的拿cool的得分
贡献为\(floor(c/mid)*cal(200,25,10,mid)\)
如果c%mid==0,直接计算前floor(c/mid)段为cool,后floor(n/(mid+1))-floor(c/mid)段为good的贡献
为\(cal(200,25,10,mid)*floor(c/mid)+cal(100,50,5,mid)*(floor(n/(mid+1))-floor(c/mid))\)
如果c%(mid)!=0,还需要加上最后这段既有cool也有good的贡献
前面有num=c%mid个cool,得分为\(cal(200,25,10,num)\),积累的JAM有floor(num/25)个了,未凑齐的JAM拿到了num%25个cool,贡献是\((num\%25)/25 = 2*(num\%25)/50\),还需要\(need=50-2*(num\%25)\)个good才能凑成一个JAM
这段中剩下的good有mid-c个,
1.1
如果够need个(mid-num>=need),可以凑成一个JAM,也就是要从mid-num中减去need个,这need个产生的贡献是\((100+5*floor(num/25))*need\)
剩下的good的个数为left=mid-num-need,这里已经有floor(num/25)+1个JAM了,起始分数ori要改成\(newori=(100+5*(floor(num/25)+1))\),所以贡献为\(cal(newori,50,5,left)\)
1.2
如果不够need个(mid-num<need),在现有的JAM上加成,也就是floor(num/25)个JAM,会给good加成5分,此时已经加成了\(5*floor(num/25)\),所以剩下的mid-num个good得分是\((100+5*floor(num/25))*(mid-num)\)
2.若n%(mid+1)!=0,分成总共cnt+1段,在前cnt=floor(n/(mid+1))段的贡献,计算方法如上
最后一段,需要加上\(n\%(mid+1)\)个good的贡献,也就是说,要加上\(cal(100,50,5,n\%(mid+1))\)
综上,先算前cnt=floor(n/(mid+1))段的贡献,【视为完整的,每段都是mid+1长度,每段最后一个为miss】
如果\(c>=n-n\%(mid+1)-cnt\),前cnt段能拿满cool,贡献为\(cal(200,25,10,mid)*floor(n/(mid+1))\)
最后加上n%(mid+1)这最后一段的贡献,
如果n%(mid+1)==0,跳过;
如果\(c-cnt*mid>=n%(mid+1)\),贡献为\(cal(200,25,10,n\%(mid+1))\)
如果\(c-cnt*mid<n%(mid+1)\),说明最后一段需要按good和cool混合处理。cool的数量为\(c-cnt*mid\),good的数量为\(n%(mid+1)-(c-cnt*mid)\)。
贡献为\(mix(c-cnt*mid,n\%(mid+1)-(c-cnt*mid))\)
函数:mix(cool,good)
分类讨论是cool在前还是good在前
1.cool在good前:
cool贡献了cal(200,25,10,cool),cool积累的JAM已经有了floor(cool/25)个,剩下cool%25个cool没凑成完整的JAM,需要need=\(50-2*cool\%25\)个good,本段剩下的good总共有good个。
若good<need,这部分good的贡献是\((100+5*floor(cool/25))*good\)
若good>=need,这部分good的贡献是\((100+5*floor(cool/25))*need\)
剩下lef=good-need个good,这部分需要计算\(newori=100+5*(floor(cool/25)+1)\),然后\(cal(newori,50,5,lef)\)
2.good在cool前:
good贡献了cal(100,50,5,good),good积累的JAM已经有floor(good/50)个,剩下good%50个没凑成JAM,需要need=50-good%50个good,因为cool的贡献是good的两倍,所以需要need/2个cool。此处need/=2
若cool<need,这部分cool贡献是\((200+10*floor(good/50))*cool\)
若cool>=need,这部分cool贡献是\((200+10*floor(good/50))*need\)
剩下lef=cool-need个cool,需要计算\(newori=200+10*(floor(good/50)+1)\),然后\(cal(newori,25,10,lef)\)
取上述两种情况的最大值
如果\(c<n-n\%(mid+1)-cnt\),拿不满cool,
先计算前cnt【完整】段:
先计算拿满cool的,贡献为\(floor(c/mid)*cal(200,25,10,mid)\)
如果\(c\%mid==0\),再计算拿good的贡献为\(cal(100,50,5,mid)*(floor(n/(mid+1))-floor(c/mid))\)
如果\(c\%mid!=0\),最后一段拿cool又拿good,贡献为\(mix(c\%mid,mid-c\%mid)\)
再计算最后一段n%(mid+1)这段全为good的贡献,为\(cal(100,50,5,n\%(mid+1))\)
wa了的代码
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
ll c,n,s;
ll cal(ll ori,ll cir,ll bonus,ll mid)
{
ll res=0;
res+=ori*cir*(mid/cir);
res+=bonus*cir*((mid/cir)-1)*(mid/cir)/2;
res+=(ori+bonus*(mid/cir))*(mid%cir);
return res;
}
ll mix(ll cool,ll good)
{
ll res1=0,res2=0;
res1+=cal(200,25,10,cool);
int need=50-2*cool%25;
if(good<need) res1+=(100+5*(cool/25))*good;
else
{
res1+=(100+5*(cool/25))*need;
int lef=good-need;
ll newori=100+5*(cool/25+1);
res1+=cal(newori,50,5,lef);
}
res2+=cal(100,50,5,good);
need=50-good%50;need/=2;
if(cool<need) res2+=(200+10*(good/50))*cool;
else
{
res2+=(200+10*(good/50))*need;
int lef=cool-need;
ll newori=200+10*(good/50+1);
cal(newori,25,10,lef);
}
return max(res1,res2);
}
bool check(int mid)
{
int cnt=n/(mid+1),last=n%(mid+1);
ll score=0;
if(c>=n-last-cnt)
{
score+=cal(200,25,10,mid)*cnt;
if(last!=0)
{
if(c-cnt*mid>=last) score+=cal(200,25,10,last);
else score+=mix(c-cnt*mid,last-(c-cnt*mid));
}
}
else
{
score+=cal(200,25,10,mid)*(c/mid);
if(c%mid==0) score+=cal(100,50,5,mid)*(cnt-c/mid);
else score+=mix(c%mid,mid-c%mid);
score+=cal(100,50,5,last);
}
return score>=s;
}
int main()
{
//printf("%lld\n",cal(100,50,5,65));
while(~scanf("%lld%lld%lld",&n,&c,&s))
{
int l=0,r=n,ret=-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) r=mid-1,ret=mid;
else l=mid+1;
}
if(ret==-1) puts("impossible");
else printf("%d\n",ret);
}
return 0;
}

浙公网安备 33010602011771号