Loading

【题解】[NOI2017] 蔬菜

这题可以有更简单的建模。

先构造费用流,对于每一天建一个点,第 \(i\) 天记为点 \(i\),下文为方便表述,记 \((u,v,c,w)\) 表示从点 \(u\) 连向 \(v\) 容量为 \(c\),费用为 \(w\) 的边。

每天卖出 \(m\) 颗菜,连边 \((S,i,m,0)\)。对于每种蔬菜,第 \(i\) 天有 \(x\) 颗坏掉,就连边 \((i,T,x,a_i)\)。第 \(i\) 天可以卖出在 \(\ge i\) 天坏掉的菜,连边 \((i,i+1, \inf, 0)\)

直接跑最大费用最大流即可求出答案。

同时我们观察这张图,发现它非常优美,除了源点和汇点,其它的点刚好排成一排,并且有边 \((i,i+1,inf,0)\),从源点出发的边,或出发到汇点的边都不可能被退流,所以唯一可能退流的只有 \((i,i+1)\) 之间的边。

这样已经可以简单维护了,我们直接用线段树维护 \((i,i+1)\) 之间的反向边容量即可(支持区间加,区间求最小值)。需要注意的是对于 \(s_i\) 的条件,我们只用将第 \(i\) 种蔬菜最后一颗坏掉的菜的价格加上 \(s_i\) 即可。从前往后枚举每一天,并按价格从大到小依次考虑每种蔬菜。对于蔬菜理论可以直接双指针维护,代码为方便直接写了堆。

复杂度 \(\mathcal{O}(nm\log n)\),瓶颈在于线段树。

#define N 100005
int n, m, k, t, u[N], s[N], c[N], b[N], v[N], p[N]; LL ed[N];
priority_queue<Pr>q;
struct node{int l, r, val, tag;}a[N << 2];
const int inf = 0x7fffffff;
#define L a[x].l
#define R a[x].r
#define ls (x << 1)
#define rs (ls | 1)
#define S a[x].val
#define T a[x].tag
void build(int x,int l,int r){
	L = l, R = r;
	if(l == r)return;
	int mid = (l + r) >> 1;
	build(ls, l, mid), build(rs, mid + 1, r);
}
inline void pushup(int x,int w){T += w, S += w;}
inline void down(int x){if(T)pushup(ls, T), pushup(rs, T), T = 0;}
void ins(int x,int l,int r,int w){
	if(L >= l && R <= r)pushup(x, w);
	else{
		down(x); int mid = (L + R) >> 1;
		if(mid >= l)ins(ls, l, r, w);
		if(mid < r)ins(rs, l, r, w);
		S = min(a[ls].val, a[rs].val);
	}
}
int ask(int x,int l,int r){
	if(L >= l && R <= r)return S;
	down(x); int mid = (L + R) >> 1, mn = inf;
	if(mid >= l)cmn(mn, ask(ls, l, r));
	if(mid < r)cmn(mn, ask(rs, l, r));
	return mn;
}
inline int day(int id){
	if(b[id] == 0)return k;
	return (c[id] - v[id]) / b[id] + 1;
}
int main(){
	read(n, m, t);
	rp(i, n){
		read(u[i], s[i], c[i], b[i]), v[i] = 1;
		q.push({s[i] + u[i], i});
	}
	rp(i, t)read(p[i]), cmx(k, p[i]);
	build(1, 1, k);
	rp(i, k){
		int res = m;
		while(res--){
			while(!q.empty()){
				Pr cur = q.top(); q.pop();
				int id = cur.se, d = day(id);
				if(d >= i){
					ed[i] += cur.fi, ++v[id];
					if(v[id] <= c[id])q.push({u[id], id});
					if(d > i)ins(1, i, d - 1, 1);
					break;
				}
				else{
					int w = ask(1, d, i - 1);
					if(w){
						ed[i] += cur.fi, ++v[id];
						if(v[id] <= c[id])q.push({u[id], id});
						ins(1, d, i - 1, -1);
						break;
					}
				}
			}
			if(q.empty())break;
		}
		ed[i] += ed[i - 1];
	}
	rp(i, t)printf("%lld\n", ed[p[i]]);
	return 0;
}
posted @ 2022-08-11 16:43  7KByte  阅读(113)  评论(0编辑  收藏  举报