EOJ Monthly 2018.9 (based on Trial Round #3) E.双人旋转赛车(二分+(贪心/单指针/线段树/主席树))

题目

oxx 和 Xiejiadong 在玩一个双人旋转赛车的小游戏。

他们将进行一些比赛。每局比赛必须按顺序进行,胜者会得到该局对应的分数 xi。

由于 oxx 技艺不精(每局都可以由 Xiejiadong 决定胜负),

因此他给自己设置了初始分数 k,希望自己能够一直领先 Xiejiadong。

不过 Xiejiadong 识破了 oxx 的诡计,现在 Xiejiadong 想知道自己至少需要赢几局才可以使得自己能够在某一时刻的比分不落后于 oxx。

若无法达到则输出 −1。

n<=1e6,k<=1e9,1<=xi<=1e9

思路来源

https://acm.ecnu.edu.cn/blog/entry/239/?tdsourcetag=s_pctim_aiomsg

题解

题解写的够优秀了,我就不BB了,直接粘代码

代码1(解法2:单指针,O(nlogn))

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll; 
const int maxn=1e6+10; 
int n,k,Rank[maxn];
int tmp[maxn];//临时储存数量 
int pointer,now;//指针(pointer,n] 当前k个值都在后面 
int l,r;
struct node
{
	int a,id,rk;
}e[maxn];
bool operator<(node a,node b)
{
	return a.a<b.a||(a.a==b.a&&a.id<b.id);
}
bool cmp(node a,node b)
{
	return a.id<b.id;
}
bool ok(int mid,int k) 
{
	//printf("mid:%d\n",mid);
	ll sum=0,score=k;
	pointer=now=0;
	memset(tmp,0,sizeof tmp); 
	for(int i=1;i<=n;++i)
	{
	 if(e[i].rk>pointer&&now<=mid)
	 {
	 	tmp[e[i].rk]++;
	 	now++;
	 	sum+=Rank[e[i].rk];
	 }
	 else score+=Rank[e[i].rk];
	 while(now>mid&&pointer<n)
	 {
	 	pointer++;
	 	if(tmp[pointer])
	 	{
	 		tmp[pointer]--;
	 		now--;
	 		score+=Rank[pointer];
	 		sum-=Rank[pointer];
	 	}
	 }
	 //printf("rk:%d v:%lld\n",e[i].rk,e[i].a);
	 //printf("sum:%lld score:%lld\n",sum,score);
	 if(sum>=score)return 1;
    }
    return 0;
}
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;++i)
	{
	 scanf("%d",&e[i].a);
	 e[i].id=i;
    }
    sort(e+1,e+n+1);
    for(int i=1;i<=n;++i)
    {
     e[i].rk=i;
     Rank[i]=e[i].a;
    }
    sort(e+1,e+n+1,cmp);
    l=0;r=n;
    while(l<r)
    {
    	int mid=(l+r)/2;
    	if(!ok(mid,k))l=mid+1;
    	else r=mid;
    }
    if(!ok(l,k))puts("-1");
    else printf("%d\n",l);
	return 0;
} 

代码2(解法3:贪心)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn=1e6+10; 
//贪心 每次把最小的还给oxx 
//优先队列表示在队列中的 是xiejiadong要选的 
priority_queue<ll,vector<ll>,greater<ll> >q; 
ll sum1,sum2,a[maxn];
int n,now,ans;
int main()
{
	scanf("%d%lld",&n,&sum1);
	for(int i=1;i<=n;++i)
	scanf("%lld",&a[i]);
	ans=n+1;
	for(int i=1;i<=n;++i)
	{
		sum2+=a[i]; 
		q.push(a[i]);
		now++;//now=q.size()
		while(sum2-q.top()>=sum1+q.top())
		{
			sum2-=q.top();
			sum1+=q.top();
			q.pop();
			now--;
		}
		if(sum2>=sum1)ans=min(ans,now); 
	}
	printf("%d\n",ans==n+1?-1:ans);
	return 0;
} 

代码3(解法4:树状数组)

 

代码4(解法5:主席树)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值