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