题目链接
(BZOJ) 大人,时代变了
(Gym) https://codeforces.com/gym/101190
(Luogu) https://www.luogu.com.cn/problem/P6967
(LOJ) https://loj.ac/p/6079
题解
想了一晚上,终于有点理解了,好神仙啊。
我只会纯网络流的做法,并不会线性规划。
首先题意显然是有一个序列每个位置可以选 (0) 或 (1),收益分别是 (a_i) 和 (b_i),且满足每个长度为 (m) 的区间里选 (1) 的个数在 ([L,R]) 内。
区间个数的限制似乎在网络流中难以体现,于是有一种思路是把区间和点进行转化。也就是我们有 ((n-m+1)) 种区间,不妨设按照左端点编号,那么对于每个点,如果选了 (1) 会给编号在一个区间内的区间的选 (1) 个数增加 (1)(或者选 (0) 的个数减少 (1))。这个在网络流中的体现是用一条边从 (i) 指向 ((i+m)),分走 (1) 的流量,后面再还回来。
那么就有了一个上下界费用流的建图:建立源点 (S) 和 (0,1,2,...,n-m+1),连边 ((S,0,[0,m],0),(i,i+1,[n-R,n-L],0),(max(0,i-m),min(n-m+1,i),[0,1],b_i-a_i)),然后求 (S) 到 ((n-m+1)) 的最大费用最大流,答案再加上 (sum^n_{i=1}a_i). 一单位的流量流过 ((i,i+1)) 代表选了 (0),否则代表选了 (1).
但这样似乎并不优美。考虑优化成不带上下界的费用流。
我们其实没有必要拘泥于让 ((i,i+1)) 流过一单位流量代表选了 (0). 我们只要把 (1) 的选出来就好了!我们从源点往 (0) 连流量为 (R) 的边,代表任何时刻不可能分走超过 (R) 的流量。然后再将 ((i,i+1)) 的边流量设置为 (R-L),意味着至少有 (L) 的流量要被分走。
于是做完了,时间复杂度 (O(MFMC(n,2n))).
代码
太久没写网络流了,甚至拉完板子后忘记了边数要初始化成 (1)……
#include<bits/stdc++.h>
#define llong long long
#define mkpr make_pair
#define x first
#define y second
#define iter iterator
#define riter reverse_iterator
#define y1 Lorem_ipsum_
#define tm dolor_sit_amet_
using namespace std;
inline int read()
{
int x = 0,f = 1; char ch = getchar();
for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
return x*f;
}
namespace NetFlow
{
const int N = 1002;
const int M = 2002;
const llong INF = 1e13;
struct Edge
{
int u,v,nxt,w; llong c;
} e[(M<<1)+3];
int fe[N+3];
llong dis[N+3];
int que[N+5];
bool inq[N+3];
int lst[N+3];
int n,m,en,s,t; llong mf,mc;
void addedge(int u,int v,int w,llong c)
{
en++; e[en].u = u,e[en].v = v,e[en].w = w,e[en].c = c;
e[en].nxt = fe[u]; fe[u] = en;
en++; e[en].u = v,e[en].v = u,e[en].w = 0,e[en].c = -c;
e[en].nxt = fe[v]; fe[v] = en;
}
bool spfa()
{
for(int i=1; i<=n; i++) dis[i] = -INF;
int hd = 1,tl = 2; que[1] = s; dis[s] = 0; inq[s] = true;
while(hd!=tl)
{
int u = que[hd]; hd++; if(hd>n+1) hd-=n+1;
for(int i=fe[u]; i; i=e[i].nxt)
{
int v = e[i].v;
if(e[i].w>0&&dis[e[i].v]<dis[u]+e[i].c)
{
dis[e[i].v] = dis[u]+e[i].c; lst[e[i].v] = i;
if(!inq[e[i].v])
{
inq[e[i].v] = true;
que[tl] = e[i].v; tl++; if(tl>n+1) tl-=n+1;
}
}
}
inq[u] = false;
}
return dis[t]!=-INF;
}
void calcflow()
{
int flow = 1e5;
for(int u=t; u!=s; u=e[lst[u]].u)
{
flow = min(flow,e[lst[u]].w);
}
for(int u=t; u!=s; u=e[lst[u]].u)
{
e[lst[u]].w -= flow; e[lst[u]^1].w += flow;
}
mf += flow; mc += 1ll*flow*dis[t];
}
llong mfmc(int _n,int _s,int _t)
{
n = _n,s = _s,t = _t; mf = 0,mc = 0ll;
while(spfa()) {calcflow();} return mc;
}
}
using NetFlow::addedge;
using NetFlow::mfmc;
const int mxN = 1000;
int n,m,al,ar;
llong a[mxN+3],b[mxN+3];
int main()
{
n = read(),m = read(),ar = m-read(),al = read(); NetFlow::en = 1;
for(int i=1; i<=n; i++) a[i] = read(); for(int i=1; i<=n; i++) b[i] = read();
llong ans = 0ll;
for(int i=1; i<=n; i++) ans += a[i];
addedge(1,2,ar,0);
for(int i=1; i<=n; i++)
{
addedge(max(0,i-m)+2,min(n-m+1,i)+2,1,b[i]-a[i]);
}
for(int i=0; i<n-m+1; i++)
{
addedge(i+2,i+1+2,ar-al,0);
}
ans += mfmc(n-m+3,1,n-m+1+2);
printf("%lld
",ans);
for(int i=1; i<=n; i++)
{
if(NetFlow::e[2*i+2].w==0) {printf("E");} else {printf("S");}
}
puts("");
return 0;
}