[AGC036D] Negative Cycle 题解

很有 AT 风格的一道题,神秘的差分约束。


考虑无负环等价于差分约束有解,所以我们给每个点都附一个权值 \(x_i\)

因为初始边不能删,所以 \(x_i-x_{i+1}\ge 0\)。我们设 \(q_i=x_i-x_{i+1}\ge 0\)

由于无负环等价于环上正边数量大于负边,所以 \(i<j\) 时,必有 \(x_i-x_j=\sum\limits_{k=i}^{j-1}q_k\ge 1\);反之则有 \(x_j-x_i=\sum\limits_{k=j}^{i-1}q_k\le 1\)

因为我们想要尽可能的保留下每一条边,所以只有在 \(\sum\limits_{k=i}^{j-1}q_k=0\) 时删除负边,在 \(\sum\limits_{k=j}^{i-1}q_k\ge 2\) 时删除正边。那么这个问题就转化为了构造最优的 \(q\) 数组。

首先由于尽可能保留边,所以 \(q_i\in\{0,1\}\)。然后就有经典 \(dp\) 了。

\(dp_{i,j}\) 表示最后一个 \(1\)\(i\) 位置,倒数第二个 \(1\)\(j\) 位置时的最优解。考虑可以从 \(dp_{j,k}\) 推导过来。那么此时新增的值有:

  1. \(i+1,j+1\) 之间的所有负边。
  2. \(j+2\)\(i\) 间所有点向 \(1\)\(k\) 间连的所有正边。
  3. \(i+1\) 这个点向 \(1\)\(j\) 间连的所有正边。

系数可以通过二维前缀和简单求解。之所以有这么多 \(+1,+2\),是因为我们的 \(q\) 数组是差分数组,要比原数组长度少一。时间复杂度 \(O(n^3)\)。(感觉有很强的单调性,能不能使用斜率优化 \(dp\) 优化到 \(O(n^2)\)?)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=505;
int n,dp[N][N],a[N][N],b[N][N],ans;
int as(int xa,int xb,int ya,int yb){
	return a[xb][yb]-a[xb][ya-1]-a[xa-1][yb]+a[xa-1][ya-1];
}int bs(int xa,int xb,int ya,int yb){
	return b[xb][yb]-b[xb][ya-1]-b[xa-1][yb]+b[xa-1][ya-1];
}signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0),cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++) cin>>a[i][j];
		for(int j=i+1;j<=n;j++) cin>>b[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];
			b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
		}
	ans=bs(1,n,1,n);
	for(int i=1;i<n;i++){
		for(int j=1;j<i;j++){
			dp[i][j]=1e18;
			for(int k=0;k<j;k++)
				dp[i][j]=min(dp[i][j],dp[j][k]+as(j+2,i,1,k));
			dp[i][j]+=as(i+1,i+1,1,j)+bs(j+1,i,j+1,i);
			ans=min(ans,dp[i][j]+bs(i+1,n,i+1,n)+as(i+2,n,1,j));
		}dp[i][0]=bs(1,i,1,i);
		ans=min(ans,dp[i][0]+bs(i+1,n,i+1,n));
	}return cout<<ans,0;
}
posted @ 2025-03-24 17:56  长安一片月_22  阅读(23)  评论(0)    收藏  举报