题解 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;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/solution-AGC036D.html