title
简化题意:
定义了一种蔬菜为:\(ai,si,ci,xi\),意思是蔬菜的价格为 \(a_i\),第一份卖出时价格为 \(a_i+s_i\),一共有 \(c_i\) 份,每天会有 \(x_i\) 份过期;每天最多卖出 \(m\) 份蔬菜,多组输入天数依次最大化收入。
analysis
首先,这道题目有一个费用流的写法(和今年 \(NOI2019D1T3\) 一样)。
因为保质期不好限制,我们把每种蔬菜都按照过期时间分成若干类,每类大概 \(x\) 个,这样就相当于我们有了 \(n\times p\) 种蔬菜;每种蔬菜在指定时间过期;我们把 \(s\) 的奖励放到每一种蔬菜过期时间最晚的那一类中去。
所以建图如下:
- \(源点s\) 向 \(每一天i\) 连边,容量为 \(m\),费用为 0;
- \(每一天i\) 向 \(所有在第i天过期的蔬菜\) 连边,容量为为 \(inf\),费用为 \(a\);
- \(每一天i\) 向 \(下一天i+1\) 连边,容量为 \(inf\),费用为 0;
- \(每种蔬菜\) 向 \(汇点t\) 连边,容量为 \(个数\),费用为 0;
- 针对每一种蔬菜的最后一类,我们分出来 1 个流量建出费用为 \(s\) 的边。
这样点数是 \(O(np+p)\),边数是\(O(np+n+p)\)的。
一天一天的增广费用流,可以跑过前 \(60pts\) (不知道为什么我的费用流写的有些丑,只跑过了 \(52pts\) ) 。
然后性质\(1,2\) 直接贪心可以再搞 \(16pts\) 。
然后就可以考虑正解了。
我们发现对于 \(p=i\) 时,我们所选择的蔬菜,一定是 \(p=i+1\) 时所选择的蔬菜的子集。这样我们跑费用流的时候,每次增加一个新点的时候是不会退流的。所以是可以贪心模拟费用流的增广过程,并且用数据结构维护一下就好了。
所以想法就多了,可以用堆,线段树,并查集,\(etc.\)
我写了堆和并查集(这个代码长度极其好评)的,堆的写法,我觉得看看 yyb 大爷的 \(blog\) 最好了。
并查集的写法很神奇,这种写法是在考虑卖的菜,贵菜先卖,但同时菜也要尽量在变质的时候再卖,然后用并查集维护最近的还能卖的天数,把菜尽量往后塞,如果位置没了,就说明这颗菜不需要了。
code
费用流\(60pts~+\)性质\(1\&\&\)性质\(2~16pts\)
#include<bits/stdc++.h>
#define mp make_pair
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=1e5+10,maxm=1e6+10,inf=0x3f3f3f3f;
char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
char Out[1<<24],*fe=Out;
inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
template<typename T>inline void write(T x)
{
if (!x) *fe++=48;
if (x<0) *fe++='-', x=-x;
T num=0, ch[20];
while (x) ch[++num]=x%10+48, x/=10;
while (num) *fe++=ch[num--];
*fe++='\n';
}
struct QwQ{int a,s,c,x;}f[maxn];
struct Orz{int id,x;}q[maxn];
inline bool operator < (Orz a,Orz b)
{
return a.x<b.x;
}
int n,m,k;
ll ans[maxn];
namespace solve1
{
inline bool cmp(QwQ a,QwQ b)
{
return a.a>b.a;
}
inline void G()
{
sort(f+1,f+n+1,cmp);
int now=1; ll res=0;
for (int i=1; i<=k; ++i)
{
int tmp=(q[i].x-q[i-1].x)*m;
while (tmp && now<=n)
{
if (f[now].c>=tmp)
{
f[now].c-=tmp; res+=(ll)tmp*f[now].a;
if (!f[now].c) ++now;
break;
}
tmp-=f[now].c; res+=(ll)f[now].c*f[now].a;
++now;
}
ans[q[i].id]=res;
}
for (int i=1; i<=k; ++i) write(ans[i]);
flush();
}
}
namespace solve2
{
pii num[maxn<<1];
inline bool cmp(pii a,pii b)
{
return a.first>b.first;
}
inline void G()
{
int tot=0;
for (int i=1; i<=n; ++i)
{
num[++tot]=mp(f[i].a+f[i].s,1);
if (f[i].c-1) num[++tot]=mp(f[i].a,f[i].c-1);
}
sort(num+1,num+tot+1,cmp);
int now=1; ll res=0;
for (int i=1; i<=k; ++i)
{
int tmp=(q[i].x-q[i-1].x)*m;
while (tmp && now<=tot)
{
if (num[now].second>=tmp)
{
num[now].second-=tmp; res+=(ll)tmp*num[now].first;
if (!num[now].second) ++now;
break;
}
tmp-=num[now].second; res+=(ll)num[now].second*num[now].first;
++now;
}
ans[q[i].id]=res;
}
for (int i=1; i<=k; ++i) write(ans[i]);
flush();
}
}
namespace solve3
{
int ver[maxm*7],edge[maxm*7],Next[maxm*7],cost[maxm*7],head[maxm],len=1;
inline void add(int x,int y,int z,int c)
{
ver[++len]=y,edge[len]=z,cost[len]=c,Next[len]=head[x],head[x]=len;
ver[++len]=x,edge[len]=0,cost[len]=-c,Next[len]=head[y],head[y]=len;
}
int s,t,tot=0,pre[maxm];
ll dist[maxm];
bool vis[maxm];
inline bool spfa()
{
memset(dist,-inf,sizeof(dist));
memset(pre,0,sizeof(pre));
deque<int>q;q.push_back(s);dist[s]=0;
while (!q.empty())
{
int x=q.front();
q.pop_front();
vis[x]=0;
for (int i=head[x]; i; i=Next[i])
{
if (!edge[i]) continue;
int y=ver[i];
if (dist[y]<dist[x]+cost[i])
{
dist[y]=dist[x]+cost[i]; pre[y]=i;
if (!vis[y])
{
if (!q.empty() && dist[q.front()]<dist[y]) q.push_front(y);
else q.push_back(y);
vis[y]=1;
}
}
}
}
return pre[t];
}
int id[1010][1010];
inline void G()
{
s=0,t=++tot;
for (int i=1; i<=n; ++i)
{
for (int j=1; j<q[k].x; ++j)
{
id[i][j]=++tot;
if (j!=1) add(id[i][j-1],id[i][j],inf,0);
if (f[i].c<=f[i].x) { add(tot,t,f[i].c-1,0),f[i].c=0; break; }
f[i].c-=f[i].x;
add(id[i][j],t,f[i].x,0);
}
if (f[i].c)
{
int j=q[k].x;
id[i][j]=++tot;
if (j!=1) add(id[i][j-1],id[i][j],inf,0);
add(tot,t,f[i].c-1,0),f[i].c=0;
}
add(tot,t,1,f[i].s);
}
int now=1; ll res=0;
for (int i=1; i<=k; ++i)
{
while (now<=q[i].x)
{
add(s,++tot,m,0);
for (int j=1; j<=n; ++j)
if (id[j][now]) add(tot,id[j][now],inf,f[j].a);
++now;
}
while (spfa())
{
int cur=t,low=inf;
while (pre[cur]) low=min(low,edge[pre[cur]]),cur=ver[pre[cur]^1];
res+=(ll)low*dist[t]; cur=t;
while (pre[cur]) edge[pre[cur]]-=low,edge[pre[cur]^1]+=low,cur=ver[pre[cur]^1];
}
ans[q[i].id]=res;
}
for (int i=1; i<=k; ++i) write(ans[i]);
flush();
}
}
int main()
{
read(n);read(m);read(k);
bool f1=1, f2=1;
for (int i=1; i<=n; ++i)
{
read(f[i].a),read(f[i].s),read(f[i].c),read(f[i].x);
if (f[i].x) f1=0;
if (f[i].s) f2=0;
}
for (int i=1; i<=k; ++i) read(q[i].x),q[i].id=i;
sort(q+1,q+k+1);
if (f1 && f2) solve1::G();
else if (f1) solve2::G();
else solve3::G();
return 0;
}
并查集维护
#include<bits/stdc++.h>
#define mp make_pair
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=1e6+10,inf=1e5;
char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
char Out[1<<24],*fe=Out;
inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
template<typename T>inline void write(T x)
{
if (!x) *fe++=48;
if (x<0) *fe++='-', x=-x;
T num=0, ch[20];
while (x) ch[++num]=x%10+48, x/=10;
while (num) *fe++=ch[num--];
*fe++='\n';
}
typedef int iarr[maxn];
iarr a,s,c,x,fa,num;
inline int get(int x)
{
return fa[x]==x?x:fa[x]=get(fa[x]);
}
ll ans[maxn];
priority_queue<pii>q;
int main()
{
int n,m,k;read(n);read(m);read(k);
for (int i=1; i<=n; ++i) read(a[i]),read(s[i]),read(c[i]),read(x[i]),q.push(mp(a[i]+s[i],i));
for (int i=1; i<=inf; ++i) fa[i]=i,num[i]=m;
int cnt=0,day;
while (!q.empty())
{
int profit=q.top().first,t=q.top().second;
q.pop();
if (!x[t]) day=get(inf);
else day=get(min(inf,(c[t]-1)/x[t]+1));
if (!day) continue;
--c[t],--num[day],++cnt;
if (!num[day]) fa[day]=get(day-1);
if (c[t]) q.push(mp(a[t],t));
ans[cnt]=ans[cnt-1]+profit;
}
for (int i=1,p; i<=k; ++i) read(p),write(ans[min(cnt,m*p)]);
flush();
return 0;
}
堆维护
#include<bits/stdc++.h>
#define mp make_pair
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=1e5+10,inf=1e5;
char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
char Out[1<<24],*fe=Out;
inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
template<typename T>inline void write(T x)
{
if (!x) *fe++=48;
if (x<0) *fe++='-', x=-x;
T num=0, ch[20];
while (x) ch[++num]=x%10+48, x/=10;
while (num) *fe++=ch[num--];
*fe++='\n';
}
pii Stack[maxn];int top,sum;
typedef int iarr[maxn];
iarr a,s,c,x,num;
bool vis[maxn];
ll ans[maxn];
vector<int>d[maxn];
priority_queue<pii>Q;
int main()
{
int n,m,k;read(n);read(m);read(k);
for (int i=1; i<=n; ++i) read(a[i]),read(s[i]),read(c[i]),read(x[i]);
for (int i=1; i<=n; ++i)
if (!x[i]) d[inf].push_back(i);
else d[min(inf,(c[i]+x[i]-1)/x[i])].push_back(i);
for (int i=inf; i; --i)
{
for (int j=0; j<d[i].size(); ++j) Q.push(mp(a[d[i][j]]+s[d[i][j]],d[i][j]));
if (Q.empty()) continue;
for (int j=m; j && !Q.empty(); )
{
int profit=Q.top().first,t=Q.top().second;
Q.pop();
if (!vis[t])
{
vis[t]=1;
ans[inf]+=profit;
++num[t], --j;
if (c[t]>1) Q.push(mp(a[t],t));
}
else
{
int rest=min(j,c[t]-num[t]-(i-1)*x[t]);
ans[inf]+=1ll*rest*profit;
num[t]+=rest, j-=rest;
if (num[t]^c[t]) Stack[++top]=mp(a[t],t);
}
}
while (top) Q.push(Stack[top--]);
}
while (!Q.empty()) Q.pop();
for (int i=1; i<=n; ++i) sum+=num[i];
for (int i=1; i<=n; ++i)
if (num[i]==1) Q.push(mp(-s[i]-a[i],i));
else if (num[i]) Q.push(mp(-a[i],i));
for (int i=inf-1; i; --i)
{
ans[i]=ans[i+1];
while (sum>i*m && !Q.empty())
{
int profit=Q.top().first,t=Q.top().second;
Q.pop(); profit*=-1;
if (num[t]>1)
{
int rest=min(sum-i*m,num[t]-1);
num[t]-=rest, sum-=rest, ans[i]-=1ll*rest*profit;
if (num[t]==1) Q.push(mp(-a[t]-s[t],t));
else Q.push(mp(-a[t],t));
}
else --sum, --num[t], ans[i]-=profit;
}
}
for (int i=1,p; i<=k; ++i) read(p),write(ans[p]);
flush();
return 0;
}