【题解】[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;
}