题解 AGC036D【Negative Cycle】

problem (from luogu)

有一个 \(N\) 个点的有向图,节点标号为 \(0 \sim (N - 1)\)

这张图初始时只有 \(N - 1\) 条边,每条边从 \(i\) 指向 \(i + 1\),边权为 \(0\)

对于每一对 \(i, j\)\(0 \le i, j \le N - 1\)\(i \ne j\)),Snuke 会加入新边 \(i \to j\),如果 \(i < j\) 则边权为 \(-1\),否则边权为 \(1\)

Ringo 不喜欢图中的负环,所以他想要删掉一些 Snuke 加入的边,使得最终得到的图没有负环。

但是删掉每一条边是有代价的,具体地说,删掉 \(i \to j\) 这条边,要花费 \(A_{i, j}\) 的代价。

请问满足图中不存在负环的最小删边代价是多少?

  • \(3 \le N \le 500\)\(1 \le A_{i, j} \le {10}^9\)

solution

首先第一步:不要考虑最小割模型,不要考虑网络流模型。

考虑差分约束模型:图中不存在负环,当且仅当差分约束有解。

那么题目的限制是这样的:

  • 对于 \(i<j\) 满足 \(s_i-1\geq s_j\Leftrightarrow s_i-s_j\geq 1\)。我们称之为一类边。
  • 对于 \(i>j\) 满足 \(s_i+1\geq s_j\Leftrightarrow s_j-s_i\leq 1\)。我们称之为二类边。
  • \(s_i\geq s_{i+1}\) 恒成立。

\(a_i=s_i-s_{i+1}\)(注意顺序)则显然 \(a_i\geq 0\)。改写一下:

  • 一类边:\(\sum_{i\leq k<j}a_k\geq 1\)
  • 二类边:\(\sum_{j\leq k<i}a_k\leq 1\)

考虑这么一个事:\(a_i=1\)\(a_i=2\),两个都能使原来该保留的保留,该去掉的去掉,也就是说这两个是一模一样的。

于是我们可以钦定 \(a_i\in\{0,1\}\),开始 DP。令 \(f_{i,j}\) 表示强制 \(i\) 填一,上一次填的一在 \(j\),能留下来的边的最大值。

\(calc(x,y,z)=\sum_{1\leq i\leq y}\sum_{y<j\leq z}A_{i,j}+\sum_{y<i\leq z}\sum_{x<j\leq z}B_{i,j}\) 其中 \(A,B\) 分别为一类边、二类边。这里我们钦定右端点在 \((y,z]\) 的才计算。

枚举上一次在 \(k\),那么 \(f_{i,j}=\max_{k<j}\{f_{j,k}+calc(k,j,i)\}\),边界看代码。

code

int n;
LL a[510][510],b[510][510],f[510][510];
LL sum(LL s[][510],int l,int r,int x,int y){return s[r][y]-s[l-1][y]-s[r][x-1]+s[l-1][x-1];}
LL val(int x,int y,int z){
	//.......x......y......z
//	LL res=0;
//	for(int i=1;i<=y;i++){
//		for(int j=y+1;j<=z;j++){
//			res+=a[i][j];
//		}
//	}
//	for(int i=y+1;i<=z;i++){
//		for(int j=x+1;j<=z;j++){
//			res+=b[i][j];
//		}
//	}
//	return res;
	return sum(a,1,y,y+1,z)+sum(b,y+1,z,x+1,z);
}
LL dp(){
	LL ans=0;
	for(int i=1;i<=n;i++){
		f[i][0]=val(0,0,i);
		ans=max(ans,f[i][0]+val(0,i,n+1));
		for(int j=1;j<i;j++){
			f[i][j]=-1e18;
			for(int k=0;k<j;k++){
				f[i][j]=max(f[i][j],f[j][k]+val(k,j,i));
			}
			ans=max(ans,f[i][j]+val(j,i,n+1));
		} 
	}
	return ans;
}
int main(){
	scanf("%d",&n);
	LL ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++) scanf("%lld",&b[i][j]),ans+=b[i][j];
		for(int j=i+1;j<=n;j++) scanf("%lld",&a[i][j]),ans+=a[i][j];
	}
	for(int i=1;i<=n+1;i++){
		for(int j=1;j<=n+1;j++) a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
		for(int j=1;j<=n+1;j++) b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
	}
	printf("%lld\n",ans-dp());
	return 0;
}
posted @ 2022-11-13 11:39  caijianhong  阅读(50)  评论(0编辑  收藏  举报