Codeforces 802C :Heidi and Library (hard)(网络流)

传送门

题解:
比较简单的建图法就是看做小于 k k k条流在一个 n ∗ n n*n nn序列上流,其中一些位置是必须流的,然后做个上下界费用流。

不过注意到肯定有一种方案使得这小于 k k k条流是不相交的,于是可以直接看做有 n n n个点,每个点拆点连 − ∞ -\infty 的边,然后规定这个点必须是 a i a_i ai,然后跑个最小费用可行流,这样 n n n − ∞ -\infty 必定流满,此外其他加起来最小。

最后加个 n ∗ ∞ n * \infty n即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int N=1e4+50;
const LL inf=1e14;

namespace mcmf {
	int src,des,ec=1,tl;
	int cur[N],g[N],nt[N],vt[N],c[N],walk[N],exi[N],q[N];
	LL w[N],dis[N],ans; 
	inline void add(int x,int y,int cc,LL ww) {
		nt[++ec]=g[x]; g[x]=ec; vt[ec]=y; c[ec]=cc; w[ec]=ww;
		nt[++ec]=g[y]; g[y]=ec; vt[ec]=x; c[ec]=0; w[ec]=-ww;
	}
	inline bool spfa() {
		for(int i=1;i<=des;i++) cur[i]=g[i], dis[i]=inf*100, walk[i]=0;
		q[tl=1]=src; dis[src]=0;
		for(int i=1;i<=tl;i++) {
			int u=q[i]; exi[u]=0;
			for(int e=g[u];e;e=nt[e]) 
				if(c[e] && dis[vt[e]]>dis[u]+w[e]) {
					dis[vt[e]]=dis[u]+w[e];
					if(!exi[vt[e]]) exi[vt[e]]=1, q[++tl]=vt[e];
				}
		} return dis[des]!=inf*100;
	}
	inline int dinic(int x,int f,LL cost) {
		if(x==des) {ans+=f*cost; return f;}
		int rs=0; walk[x]=1;
		for(int &e=cur[x];e;e=nt[e]) {
			if(c[e] && (dis[vt[e]]==dis[x]+w[e]) && !walk[vt[e]]) {
				int o=dinic(vt[e],min(f-rs,c[e]),cost+w[e]);
				c[e]-=o; c[e^1]+=o; rs+=o;
				if(rs==f) return rs;
			} 
		} return dis[x]=inf*100,rs;
	}
	int lim;
	inline LL mcmf() {
		while(lim && spfa() && dis[des]<0) 
			lim-=dinic(src,lim,0);
		return ans;
	}
} using mcmf::add;

int n,a[N],c[N];
int main() {
	cin>>n>>mcmf::lim;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) cin>>c[i];
	mcmf::src=2*n+1; mcmf::des=2*n+2;
	for(int i=1;i<=n;i++) 
		add(mcmf::src,i,1,c[a[i]]), add(i+n,mcmf::des,1,0), add(i,i+n,1,-inf);
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
			if(a[i]==a[j]) add(i+n,j,1,0);
			else add(i+n,j,1,c[a[j]]);
	cout<<mcmf::mcmf()+n*inf<<'\n';
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值