1. 程式人生 > 實用技巧 >Gym 101190D BZOJ 4842 Luogu P6967 LOJ #6071 [NEERC2016]Delight for a Cat (費用流)

Gym 101190D BZOJ 4842 Luogu P6967 LOJ #6071 [NEERC2016]Delight for a Cat (費用流)

題目連結

(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;
}