不得不说这是一道非常好的费用流神题。
建图清新自然,构建没有毒瘤。实为网络流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+mle n)。
- 慢洗同上,我们从(y_i)向(x_{i+n})连一条流量为(infty),费用为(s)的边。
- 然后有一个大坑点来了:每天晚上脏的餐巾不一定一定要去洗,也可以攒到第二天再洗或者直接不管了。因此我们从所有的(y_i(1le 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;
}