BZOJ 4946: [Noi2017]蔬菜 模拟费用流

title

BZOJ 4946

LUOGU 3826

简化题意:

定义了一种蔬菜为:\(ai,si,ci,xi\),意思是蔬菜的价格为 \(a_i\),第一份卖出时价格为 \(a_i+s_i\),一共有 \(c_i\) 份,每天会有 \(x_i\) 份过期;每天最多卖出 \(m\) 份蔬菜,多组输入天数依次最大化收入。

analysis

首先,这道题目有一个费用流的写法(和今年 \(NOI2019D1T3\) 一样)。

因为保质期不好限制,我们把每种蔬菜都按照过期时间分成若干类,每类大概 \(x\) 个,这样就相当于我们有了 \(n\times p\) 种蔬菜;每种蔬菜在指定时间过期;我们把 \(s\) 的奖励放到每一种蔬菜过期时间最晚的那一类中去。

所以建图如下:

  1. \(源点s\)\(每一天i\) 连边,容量为 \(m\),费用为 0;
  2. \(每一天i\)\(所有在第i天过期的蔬菜\) 连边,容量为为 \(inf\),费用为 \(a\)
  3. \(每一天i\)\(下一天i+1\) 连边,容量为 \(inf\),费用为 0;
  4. \(每种蔬菜\)\(汇点t\) 连边,容量为 \(个数\),费用为 0;
  5. 针对每一种蔬菜的最后一类,我们分出来 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;
}

转载于:https://www.cnblogs.com/G-hsm/p/11348128.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值