Codeforces Round #386 (Div. 2) F

11 篇文章 0 订阅
9 篇文章 0 订阅

题意:

Sasha开着她的车去工作,在上班路上她想听听音乐。现在她的歌单里有n首歌,规定听完第i首,Sasha能获得ai点快乐度,,Sasha当然是想获得最大的快乐度啦。。。

不过这个问题有些限制。Sasha不能一直听音乐,因为开车ks后她就会到工作点,而第i首歌耗时tis。Sasha能获得一首歌的快乐度当且仅当她听完整首歌。特别地,Sasha有w次机会,选择w首歌,只需听[ai/2](上取整)就能获得那首歌的快乐度

Sasha可以任意选择第x首歌,从这里开始听,但是从这以后不能再跳过任何一首歌。现在Sasha想要你帮忙,,给她设计一个方案使得最终获得的快乐度最大。   n <= 2E5


solution:

虽然想到了解但是场内码不完。。。假设Sasha从第x首歌开始听,因为中途不允许跳歌,所以时间减半的机会当然是都留给单曲耗时最长的w首歌曲啦。再设如果Sasha从第x首歌开始听,最多能听到第y首,那么,如果Sasha从第x+1首开始听,至少能听到第y首。也就是说,这玩意满足决策单调性

剩下的就是时时刻刻维护耗时最长的w首歌了。这里提供一种用两个堆维护的做法

首先,令x = 1,尽可能多的塞歌进去,作为初始状态

在x不断右移的时候,先删除x - 1对答案的影响,这里维护两个堆,Q1:存放当前耗时最长w首歌,Q2:存放除了那w首歌,其它在当前选定集合的歌。删除x - 1对答案的影响,若x - 1本来在Q1,那么从Q2中找到最大的填进去。右指针如果想右移,先看Q1中最短的歌是不是比它短,是就能替换,被替换的进入Q2。因此,前者小根堆,后者大根堆。因为涉及元素删改操作,可用配对堆实现。(平板电视大法好)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 2E5 + 20;

struct data{
	int num,ti; data(){}
	data(int num,int ti): num(num),ti(ti){}
	bool operator < (const data &b) const {return ti < b.ti;}
	bool operator > (const data &b) const {return ti > b.ti;}
};
typedef __gnu_pbds::priority_queue<data,greater<data>,__gnu_pbds::pairing_heap_tag> Hp1;
typedef __gnu_pbds::priority_queue<data,less<data>,__gnu_pbds::pairing_heap_tag> Hp2;

int n,w,k,ans,sa,st,R,tot,a[maxn],t[maxn];
bool inq[maxn];
Hp1 Q; Hp2 Q2;
Hp1::point_iterator id[maxn];
Hp2::point_iterator id2[maxn];

bool Add(int x)
{
	int now = st;
	if (tot < w)
	{
		now += (t[x] + 1) / 2;
		if (now > k) return 0;
		st = now; sa += a[x];
		ans = max(ans,sa); ++tot;
		id[x] = Q.push(data(x,t[x]));
		inq[x] = 1; return 1;
	}
	else
	{
		data G = Q.top();
		if (G.ti < t[x])
		{
			now -= (G.ti + 1) / 2;
			now += G.ti; now += (t[x] + 1) / 2;
			if (now > k) return 0;
			st = now; sa += a[x];
			ans = max(ans,sa);
			Q.pop(); inq[G.num] = 0;
			id[x] = Q.push(data(x,t[x]));
			id2[G.num] = Q2.push(G); 
			inq[x] = 1; return 1;
		}
		else
		{
			now += t[x];
			if (now > k) return 0;
			st = now; sa += a[x];
			ans = max(ans,sa);
			id2[x] = Q2.push(data(x,t[x]));
			return 1;
		}
	}
}

void Erase(int x)
{
	sa -= a[x];
	if (inq[x])
	{
		st -= (t[x] + 1) / 2;
		Q.erase(id[x]);
		inq[x] = 0; --tot;
	}
	else st -= t[x],Q2.erase(id2[x]);
	while (tot < w && !Q2.empty())
	{
		data G = Q2.top(); Q2.pop();
		st -= G.ti; st += (G.ti + 1) / 2;
		++tot; id[G.num] = Q.push(G); inq[G.num] = 1;
	}
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	cin >> n >> w >> k;
	for (int i = 1; i <= n; i++) scanf("%d",&a[i]);
	for (int i = 1; i <= n; i++) scanf("%d",&t[i]);
	int R = 0; while (R < n && Add(R + 1)) ++R;
	for (int i = 2; i <= n; i++)
	{
		Erase(i - 1);
		while (R < n && Add(R + 1)) ++R;
	}
	cout << ans;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值