不得不说这是一道非常好的费用流神题。
建图清新自然,构建没有毒瘤。实为网络流24题中难得一见的好题。
我们考虑一下网络流的建图。
首先拆点并建立超级源点SS和超级汇点\(T\)(常见套路),我们把每一天拆成早上\(x_i\)和晚上\(y_i\)。
然后考虑每天早上要向\(T\)提供\(r_i\)条干净的餐巾,而\(S\)要向每天晚上提供\(r_i\)条脏餐巾。
然后就是一些购买和洗的问题了,这个我们就来细致地讲一讲建边(这里边带费用)
- 同上,从\(x_i\)向\(T\)连一条流量为\(r_i\),费用为\(0\)(又没买餐巾怎么要钱?)的边。
- 同上,从\(S\)向\(y_i\)连一条流量为\(r_i\),费用为\(0\)的边。
- 考虑每天早上购买餐巾,我们从\(S\)向\(x_i\)连一条流量为\(\infty\),费用为\(p\)的边。
- 然后是快洗,我们从\(y_i\)向\(x_{i+m}\)连一条流量为\(\infty\),费用为\(f\)的边。表示可以在第\(i\)天晚上送一些餐巾去洗。需要注意的是\(i+m\le n\)。
- 慢洗同上,我们从\(y_i\)向\(x_{i+n}\)连一条流量为\(\infty\),费用为\(s\)的边。
- 然后有一个大坑点来了:每天晚上脏的餐巾不一定一定要去洗,也可以攒到第二天再洗或者直接不管了。因此我们从所有的\(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;
}