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;
}

posted @ 2020-09-05 19:09  JWizard  阅读(166)  评论(0)    收藏  举报