王子 [费用流]

题意

给定$n,k,p,q$,现在要给一段长度为n的格子染黑白,第$i$个格子染成黑色得到$a_i$分,白色$b_i$分

要求连续的$k$个格子中要有至少$p$个黑的,$q$和白的

求最大得分

思路

一眼是没想法的......瞄了一眼题解看到“网络流”三个大字,就会了

还是要多想一些方法,多试试,因为这种题看着不是dp就是什么鬼畜的转化,那转化模型肯定先看是不是网络流

考虑序列模型的经典网络流转化方法

本题中的核心限制是对于每一段$k$区间的颜色数限制

可以看到实际上这个$p,q$就是限制黑色格子的数量是$[p,k-q]$

同时这个限制对于每一个格子而言,这个格子参与$k$个区间的限制

我们考虑这样一个方法:

首先把所有的都涂成白色,然后用跨越一个$k$区间的一点流量来代表变成一个黑格子带来的影响,其他的从$i$到$i+1$的代表一个白格子

那么这样我们把白格子边设成流量上限为$k-q-p$上限,费用为0,代表额外的白变黑的数量上限

黑格子边显然容量为1,费用为对应的格子的$b_i-a_i$

一些细节注意:

首先这个跨越的黑格子边,从$i$出发,终点在$k+i$不是在$k+i-1$

然后注意最开始的$k$个点,没有前驱去到达它们,那么建立一个辅助节点$aux$,从$s->aux$连一条容量为$k-q$的(限制一下),再从$aux$连向$1$到$k$号点

实现上可以把费用流的$ans$初值设为所有白格子分数的和,然后用上面定义的费用直接算就可以了

费用流推荐使用spfa魔改版zkw多路增广,不过本题中单路多路其实差距不大,$EK$还可能更快(因为图的形态问题)

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#include<queue>
#define ll long long
using namespace std;
inline ll read(){
	ll re=0,flag=1;char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') flag=-1;
		ch=getchar();
	}
	while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
	return re*flag;
}
ll n,m,l,r,cnte=1,first[1010],dis[1010],vis[1010],ans=0,maxf=0;
struct edge{
	ll to,next,w,cap;
}a[20010];
inline void add(ll u,ll v,ll w,ll cap){
	a[++cnte]=(edge){v,first[u],w,cap};first[u]=cnte;
	a[++cnte]=(edge){u,first[v],-w,0,};first[v]=cnte;
}
queue<ll>q;
bool spfa(ll s,ll t){
	ll i,u,v;
	for(i=s;i<=t+1;i++) dis[i]=-111111111111111ll,vis[i]=0;
	vis[t]=1;dis[t]=0;q.push(t);
	while(!q.empty()){
		u=q.front();q.pop();vis[u]=0;
		for(i=first[u];~i;i=a[i].next){
			v=a[i].to;
			if(!a[i^1].cap) continue;
			if(dis[v]!=-111111111111111ll&&(dis[v]>=dis[u]+a[i^1].w)) continue;
			dis[v]=dis[u]+a[i^1].w;
			if(!vis[v]){
				vis[v]=1;
				q.push(v);
			}
		}
	}
	return dis[s]!=-111111111111111ll;
}
ll dfs(ll u,ll t,ll lim){
	if(u==t||!lim) return lim;
	ll i,v,f,flow=0;vis[u]=1;
	for(i=first[u];~i;i=a[i].next){
		v=a[i].to;if(vis[v]) continue;
		if(dis[v]==dis[u]-a[i].w&&(f=dfs(v,t,min(lim,a[i].cap)))){
			a[i].cap-=f;
			a[i^1].cap+=f;
			flow+=f;
			lim-=f;
			ans+=f*a[i].w;
			if(!lim){vis[u]=0;return flow;}
		}
	}
	dis[u]=-111111111111111ll;
	return flow;
}
void mcmf(ll s,ll t){
	while(spfa(s,t)){
		memset(vis,0,sizeof(vis));
		vis[t]=1;
		while(vis[t]){
			memset(vis,0,sizeof(vis));
			maxf+=dfs(s,t,1e9);
		}
	}
}
ll x[1010],y[1010];
int main(){
	memset(first,-1,sizeof(first));
	n=read();m=read();l=read();r=m-read();ll i;
	for(i=1;i<=n;i++) x[i]=read(),y[i]=read(),ans+=y[i];
	add(0,n+2,0,r);
	for(i=1;i<=m;i++) add(n+2,i,0,1);
	for(i=1;i<=n;i++){
		add(i,i+1,0,r-l);
		add(i,((i<=n-m)?i+m:n+1),x[i]-y[i],1);
	}
	mcmf(0,n+1);
	cout<<ans<<'\n';
}
posted @ 2018-11-26 21:49  dedicatus545  阅读(159)  评论(0编辑  收藏  举报