题目链接

(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\n",ans);
	for(int i=1; i<=n; i++)
	{
		if(NetFlow::e[2*i+2].w==0) {printf("E");} else {printf("S");}
	}
	puts("");
	return 0;
}