Topcoder 14719 - RatingProgressAward(最小割)

题面传送门

神仙最小割……好久没写过网络流了,故写题解以祭之(

首先考虑一个非常 trivial 的问题:如果知道排列顺序之后怎样计算最大值,用脚趾头想一下就能知道是原序列的最大子段和,因为每个课程之后的 rating 显然相当于前缀和,前缀和相减自然就是区间和了。

我们假设最大区间和为 \([l,r]\),那么我们考虑将序列分成三段 \([1,l-1],[l,r],[r+1,n]\),一个很显然的性质是由于限制关系不成环,因此段内部的相对位置关系是不重要的,也就是说我们只要钦定每个元素在哪一段,就总存在一种排列方式符合限制要求。而显然一个元素只有在中间一段才能对答案产生贡献。

这样就可以最小割了呗,记 \(S=\sum\limits_{i=1}^n\max(w_i,0)\),也就是 \(w\) 序列中所有正权值的和,我们考虑这样建图:将每个点拆成 \(in_i\)\(out_i\) 两个点,对于 \(w_i>0\) 的点连边 \(S\to in_i\),容量 \(w_i\)\(in_i\to out_i\),容量 \(0\)\(out_i\to T\),容量 \(w_i\);对于 \(w_i<0\) 的点连边 \(S\to in_i\),容量 \(0\)\(in_i\to out_i\),容量 \(-w_i\)\(out_i\to T\),容量 \(0\)。割掉 \(S\to in_i\) 的边表示 \(i\) 在第一段 \([1,l-1]\),割掉 \(in_i\to out_i\) 表示 \(i\) 在第二段 \([l,r]\),割掉 \(out_i\to T\) 表示 \(i\) 在第三段 \([r+1,n]\)。那么怎么处理限制关系呢?对于每对 \(a_i,b_i\) 连边 \(in_{a_i}\to in_{b_i},out_{a_i}\to out_{b_i}\),容量均为 \(\infty\) 即可。然后答案就是 \(S-\) 最小割。

const int MAXV=102;
const int MAXE=2.5e3*2;
const int INF=0x3f3f3f3f;
int n,S,T,hd[MAXV+5],to[MAXE+5],nxt[MAXE+5],cap[MAXE+5],ec=1;
void adde(int u,int v,int f){
	to[++ec]=v;cap[ec]=f;nxt[ec]=hd[u];hd[u]=ec;
	to[++ec]=u;cap[ec]=0;nxt[ec]=hd[v];hd[v]=ec;
}
int dep[MAXV+5],now[MAXV+5];
bool getdep(){
	memset(dep,-1,sizeof(dep));dep[S]=0;
	queue<int> q;q.push(S);now[S]=hd[S];
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int e=hd[x];e;e=nxt[e]){
			int y=to[e],z=cap[e];
			if(z&&!~dep[y]){
				dep[y]=dep[x]+1;
				now[y]=hd[y];q.push(y);
			}
		}
	} return ~dep[T];
}
int getflow(int x,int f){
	if(x==T) return f;int ret=0;
	for(int &e=now[x];e;e=nxt[e]){
		int y=to[e],z=cap[e];
		if(z&&dep[y]==dep[x]+1){
			int w=getflow(y,min(f-ret,z));
			ret+=w;cap[e]-=w;cap[e^1]+=w;
			if(f==ret) return ret;
		}
	} return ret;
}
int dinic(){
	int ret=0;
	while(getdep()) ret+=getflow(S,INF);
	return ret;
}
struct RatingProgressAward{
	int maximalProgress(vector<int> val,vector<int> a,vector<int> b){
		n=val.size();T=(S=n<<1|1)+1;int sum=0;
		for(int i=1;i<=n;i++){
			if(val[i-1]>0) adde(S,i,val[i-1]),adde(i,i+n,0),adde(i+n,T,val[i-1]),sum+=val[i-1];
			else adde(S,i,0),adde(i,i+n,-val[i-1]),adde(i+n,T,0);
		} for(int i=0;i<a.size();i++){++a[i];++b[i];adde(a[i],b[i],INF);adde(a[i]+n,b[i]+n,INF);}
		return sum-dinic();
	}
};
posted @ 2021-07-05 09:41  tzc_wk  阅读(132)  评论(0编辑  收藏  举报