Microtransactions (hard version) CodeForces - 1165F

原题链接
考察:贪心+二分
错误思路:
  对于每个折扣能买就买.
正确思路:
  直接求最小天数是不好求的,先二分确定天数,然后判断此天数是否能买完.用while和优先队列模拟每天干什么,对于每个折扣,最优解是在给定天数范围内,买折扣物品的天数能晚则晚,将钱留给其他物品.

Code

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define f first
#define s second
using namespace std;
typedef pair<int,int> PII;
const int N = 200010;
int n,sz[N],m,sum,backup[N],last[N];
PII p[N],t[N];
bool check(int x)
{
	int c = 0,now = 0,ss = sum,cnt = 0;
	priority_queue<PII,vector<PII>,greater<PII> > q;
	memset(last,0,sizeof last);
	for(int i=1;i<=m;i++)
		if(p[i].f<=x) last[p[i].s] = p[i].f;
	for(int i=1;i<=n;i++)
	    if(last[i]) t[++cnt] = {last[i],i};
	for(int i=1;i<=cnt;i++)
	    q.push(t[i]);
	memcpy(sz,backup,sizeof backup);
	while(now<x)
	{
		now++,c++;
		while(q.size()&&q.top().f<now) q.pop();
		while(c&&q.size()&&q.top().f==now)
		{
			PII it = q.top();
			q.pop();
			if(!sz[it.s]) continue;
			int y = min(sz[it.s],c);
			c-=y;
			sz[it.s]-=y;
			ss-=y;
			if(!c) break;
		}
	}
	if(ss&&c>=2*ss) return 1;
	if(!ss) return 1;
	return 0; 
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&sz[i]),sum+=sz[i];
	for(int i=1;i<=m;i++) scanf("%d%d",&p[i].f,&p[i].s);
	sort(p+1,p+m+1);
	memcpy(backup,sz,sizeof sz);
	int l = sum,r = sum<<1;
	while(l<r)
	{
		int mid = l+r>>1;
		if(check(mid)) r = mid;
		else l = mid+1;
	}
	printf("%d\n",r);
	return 0;
}
posted @ 2021-06-30 16:59  acmloser  阅读(47)  评论(0编辑  收藏  举报