一类贪心题乱编

PS:明天就要考数学了,好慌

我们考虑一类贪心题,它们常常是某些物品带有一定价值,若干天,每次能选一定量,但是随着一段时间可决策集合越来越窄.

对于这类题,一个比较通用的想法就是倒过来考虑,因为如果倒过来考虑,就变成决策集合单调不减,于是往往可以直接用堆之类的数据结构进行贪心.

类似一种反悔贪心?常常体现为贪心实质是在模拟费用流(然而我基本不会网络流)

例题1,[NOI2017]蔬菜

题意:太长了就不说了,自己看吧,大致就是上面的模型

发现正着做十分难做,考虑倒着贪心.

因为第p-1天答案显然是第p天减掉最劣的决策(因为p-1对于p来说决策集合单调不减,所以p能选的所有p-1都能选,所以可以随便删)

于是我们只要求出第P天的值,(P = max($p_{j}$)),然后就可以倒推1~P-1

考虑倒着推怎么做.我们发现题目一堆奇奇怪怪的限制在倒推中突然就有了意义,如果某堆食物从某天开始就不变质了,就把它加进去(又是因为决策集合单调不减,对于倒着推来说,如果当前不变质,之后就不会再变质了)

于是可以直接塞堆里,贪心

/*[NOI2017]蔬菜*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int read(){
	char c = getchar();
	int x = 0;
	while(c < '0' || c > '9')		c = getchar();
	while(c >= '0' && c <= '9')		x = x * 10 + c - 48,c = getchar();
	return x;
}
struct Node{
	ll v,id;
	Node (ll fi = 0,ll se = 0){
		v = fi,id = se;
	}
	bool operator <(const Node &a)const{
		return v < a.v;
	} 
};
const int N = 1e5 + 10;
priority_queue<Node>q;
int a[N],s[N],c[N],x[N],n,m,k;
int P = 1e5;
vector<int>d[N];/*第i天会有哪些元素加进来*/
int sel[N];
bool vis[N];
Node st[N];int top;
ll ans[N];ll ss;
#define pb push_back
int main(){
	n = read(),m = read(),k = read();
	for(int i = 1; i <= n; ++i)		a[i] = read(),s[i] = read(),c[i] = read(),x[i] = read();
	for(int i = 1; i <= n; ++i){
		if(x[i] == 0)	d[P].pb(i);
		else	d[min(P,(c[i] - 1 + x[i]) / x[i])].pb(i);
	}
	for(int i = P; i; --i){
		for(int j = 0; j < (int)d[i].size(); ++j){
			int x = d[i][j];
			q.push(Node(a[x] + s[x],d[i][j]));
		}
		for(int j = m; j && !q.empty();){
			Node now = q.top();q.pop();
			if(!vis[now.id]){
				sel[now.id]++;
				ans[P] += now.v;
				if(c[now.id] > 1)	q.push(Node(a[now.id],now.id));
				vis[now.id] = 1;
				--j;
			}
			else{
				int res = min(1ll * j,c[now.id] - 1ll * (i - 1) * x[now.id] - sel[now.id]);
				j -= res;ans[P] += 1ll * res * now.v;
				sel[now.id] += res;if(sel[now.id] != c[now.id])		st[++top] = now;
			}
		}
		while(top)	q.push(st[top]),top--;
	}
	while(q.size())		q.pop();
	memset(vis,0,sizeof(vis));
	for(int i = 1; i <= n; ++i){
		ss += sel[i];
		if(sel[i] == 0)	continue;
		if(sel[i] == 1)		q.push(Node(-a[i]-s[i],i));
		else	q.push(Node(-a[i],i));
	}
	for(int i = P - 1; i; --i){
		ans[i] = ans[i+1];
		while(ss > i * m){
			ss--;
			Node now = q.top();q.pop();
			ans[i] -= -now.v;sel[now.id]--;
			if(sel[now.id] > 1)		q.push(now);
			if(sel[now.id] == 1)	q.push(Node(-a[now.id] - s[now.id],now.id));
		}
	}
//	return 0;
	while(k--){
		int p = read();
		printf("%lld\n",ans[p]);
	} 
	return 0;
}

  upd:还有从费用流角度的理解,先咕着

例题2.[CF505E]

首先肯定是要二分的,设当前二分的值为H,所以就变成了最大值能否不超过H

有一个极度鬼畜的max($h_{i}$ - p,0)

我们依然考虑倒着做,考虑设所有值初始位置都是H,每时刻-=a[i],任意时刻都不能<0,你可以给它加上p,是否存在一种方案,使得最终位置 >=  h[i]

只要按变成<0的顺序,优先+那些会先<0的,贪心地+p最后再检查是否全部合法就可以了.

/*CF505E Mr. Kitayuta vs. Bamboos*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int read(){
	char c = getchar();
	int x = 0;
	while(c < '0' || c > '9')		c = getchar();
	while(c >= '0' && c <= '9')		x = x * 10 + c - 48,c = getchar();
	return x;
}
const int N = 1e5 + 10;
ll a[N],h[N],n,m,k,p;
struct NUM{
	int ed;ll v;int id,st;/*几天后会小于0*/
	bool operator <(const NUM &a)const{
		return ed > a.ed;
	}
	NUM (int _a = 0,ll _b = 0,int _c = 0,int _d = 0){
		ed = _a,v = _b,id = _c,st = _d;
	}
};
priority_queue<NUM>q;
bool check(ll x){
	while(q.size())		q.pop();
	for(int i = 1; i <= n; ++i){
		if(x - m * a[i] >= h[i])	continue;
		q.push(NUM(x / a[i],x,i,0));
//		cout<<i<<' '<<x / a[i]<<endl;
	}
	for(int i = 1; i <= m; ++i){
		ll res = m - i;
		for(int j = 1; j <= k; ++j){
			if(q.empty())	return 1;
			NUM now = q.top();q.pop();
			if(now.ed < i)	return 0;
			ll v = now.v - 1ll * (i - now.st) * a[now.id] + p;
			if(v - res * a[now.id] >= h[now.id])		continue;
			q.push(NUM(max((ll)i,i + v / a[now.id]),v,now.id,i));
		}
	}
	if(q.empty())	return 1;return 0;
}
int main(){
	n = read(),m = read(),k = read(),p = read();
	for(int i = 1; i <= n; ++i)		h[i] = read(),a[i] = read();
	ll l = 1,r = 1e15;
	ll ans = 0;
	while(l <= r){
		ll mid = (l + r) >> 1;
		if(check(mid)){
			ans = mid;
			r = mid - 1;
		}
		else	l = mid + 1;
	}
	cout<<ans<<endl;
	return 0;
}

  

posted @ 2021-01-27 23:18  y_dove  阅读(62)  评论(0编辑  收藏  举报