Luogu P1251 餐巾计划问题

不得不说这是一道非常好的费用流神题。

建图清新自然,构建没有毒瘤。实为网络流24题中难得一见的好题。

我们考虑一下网络流的建图

首先拆点并建立超级源点SS和超级汇点\(T\)(常见套路),我们把每一天拆成早上\(x_i\)晚上\(y_i\)

然后考虑每天早上要向\(T\)提供\(r_i\)条干净的餐巾,而\(S\)要向每天晚上提供\(r_i\)条脏餐巾。

然后就是一些购买和洗的问题了,这个我们就来细致地讲一讲建边(这里边带费用)

  1. 同上,从\(x_i\)\(T\)连一条流量为\(r_i\),费用为\(0\)(又没买餐巾怎么要钱?)的边。
  2. 同上,从\(S\)\(y_i\)连一条流量为\(r_i\),费用为\(0\)的边。
  3. 考虑每天早上购买餐巾,我们从\(S\)\(x_i\)连一条流量为\(\infty\),费用为\(p\)的边。
  4. 然后是快洗,我们从\(y_i\)\(x_{i+m}\)连一条流量为\(\infty\),费用为\(f\)的边。表示可以在第\(i\)天晚上送一些餐巾去洗。需要注意的是\(i+m\le n\)
  5. 慢洗同上,我们从\(y_i\)\(x_{i+n}\)连一条流量为\(\infty\),费用为\(s\)的边。
  6. 然后有一个大坑点来了:每天晚上脏的餐巾不一定一定要去洗,也可以攒到第二天再洗或者直接不管了。因此我们从所有的\(y_i(1\le i<n)\)\(y_{i+1}\)连一条流量为\(\infty\),费用为\(0\)的边。注意这里必须由晚上连至晚上,因为脏餐巾不能在早上使用

然后我们都要建反向边。然后直接跑EK+SPFA是可以直接过的。但注意开long long

CODE

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long LL;
const LL N=4005,INF=1e18;
queue <LL> q;
struct edge
{
    LL to,next,c,f;
}e[N<<3];
LL head[N],dis[N],cap[N],pre[N],last[N],s,t,n,x,y,cnt=-1;
bool vis[N];
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(LL &x)
{
    x=0; char ch=tc();
    while (ch<'0'||ch>'9') ch=tc();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline void add(LL x,LL y,LL c,LL f)
{
    e[++cnt].to=y; e[cnt].c=c; e[cnt].f=f; e[cnt].next=head[x]; head[x]=cnt;
}
inline void insert(LL x,LL y,LL c,LL f)
{
    add(x,y,c,f); add(y,x,0,-f);
}
inline LL min(LL a,LL b)
{
    return a<b?a:b;
}
inline bool SPFA(void)
{
    memset(dis,63,sizeof(dis));
    memset(cap,63,sizeof(cap));
    memset(pre,-1,sizeof(pre));
    memset(vis,0,sizeof(vis));
    while (!q.empty()) q.pop();
    q.push(s); vis[s]=1; dis[s]=0;
    while (!q.empty())
    {
        LL now=q.front(); q.pop(); vis[now]=0;
        for (register LL i=head[now];i!=-1;i=e[i].next)
        if (e[i].c&&dis[e[i].to]>dis[now]+e[i].f)
        {
            dis[e[i].to]=dis[now]+e[i].f;
            cap[e[i].to]=min(cap[now],e[i].c);
            pre[e[i].to]=now; last[e[i].to]=i;
            if (!vis[e[i].to]) vis[e[i].to]=1,q.push(e[i].to);
        }
    }
    return pre[t]^-1;
}
inline void MCMF(void)
{
    LL tot=0;
    while (SPFA())
    {
        tot+=cap[t]*dis[t]; LL now=t;
        while (now!=s)
        {
            e[last[now]].c-=cap[t];
            e[last[now]^1].c+=cap[t];
            now=pre[now];
        }
    }
    printf("%lld",tot);
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register LL i; read(n); s=0; t=(n<<1)+1;
    memset(head,-1,sizeof(head));
    memset(e,-1,sizeof(e));
    for (i=1;i<=n;++i)
    read(x),insert(s,i+n,x,0),insert(i,t,x,0);
    for (read(x),i=1;i<=n;++i)
    {
        insert(s,i,INF,x);
        if (i^n) insert(i+n,i+n+1,INF,0);
    }
    for (read(x),read(y),i=1;i+x<=n;++i)
    insert(i+n,i+x,INF,y);
    for (read(x),read(y),i=1;i+x<=n;++i)
    insert(i+n,i+x,INF,y);
    MCMF(); return 0;
}
posted @ 2018-06-15 16:41  空気力学の詩  阅读(125)  评论(0编辑  收藏  举报