[AGC036D] Negative Cycle

一、题目

点此看题

二、解法

首先有一个根本想不到的题意转化:不存在负环等价于存在一组合法的差分约束的解

设差分约束的合法解是 \(x_1,x_2...x_n\),我们可以把所有边转化成不等式。图上初始的 \(n-1\) 条边,带来的限制是 \(x_i\geq x_{i+1}\),可以进一步转化为差分:\(q_i=x_{i}-x_{i+1}\geq 0\)

对于边 \(i\rightarrow j(i<j)\),满足 \(x_i-1\geq x_j\),即 \(x_i-x_j\geq 1\),那么 \(q_i+q_{i+1}...q_{j-1}\geq 1\),也就是差分数组在 \([l,r)\) 的区间和如果 \(\geq 1\),那么边 \(l\rightarrow r\) 就可以保留。换句话说如果区间和 \(=0\) 那么需要删去。

对于边 \(i\rightarrow j(i>j)\),满足 \(x_i+1\geq x_j\),即 \(x_j-x_i\leq 1\),那么 \(q_j+q_{j+1}...q_{i-1}\leq 1\),也就是差分数组在 \([l,r)\) 的区间和如果 \(\leq 1\),那么边 \(r\rightarrow l\) 就可以保留。换句话说如果区间和 \(\geq 2\) 那么需要删去。

不难发现如果 \(q_i\geq 2\),那么可以调整成 \(q_i=1\),不难发现答案不变劣,所以最终的答案 \(q_i=\{0,1\}\)

那么我们可以规划每个位置上的 \(q_i\),然后删去不合法的边。根据代价计算的特性,我们可以定义 \(f[i][j]\) 表示考虑前 \(i\) 为个位置,其中 \(q_i=1\),上一个等于 \(1\) 的位置是 \(j\),需要删去边的最小权值。转移枚举 \(k\),考虑如何计算代价。

考虑左右端点都在 \((j,i]\) 中的第一类边是需要删去的。左端点在 \((k,j]\),右端点在 \((i,n]\) 的第二类边是需要删去的,这可以需处理二维前缀和轻松计算,时间复杂度 \(O(n^3)\)

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 505;
#define int long long
const int inf = 1e18;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,ans,a[M][M],b[M][M],f[M][M];
void upd(int &x,int y) {x=min(x,y);}
int val(int k,int j,int i)
{
	return b[j+1][i]+a[n][j]-a[i][j]-a[n][k]+a[i][k];
}
signed main()
{
	n=read();ans=inf;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(i^j) a[i][j]=read();
	for(int l=n;l>=1;l--)
		for(int r=l;r<=n;r++)
			b[l][r]=b[l+1][r]+b[l][r-1]
				-b[l+1][r-1]+a[l][r];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
	for(int i=1;i<n;i++)
	{
		f[i][0]=val(0,0,i);
		upd(ans,f[i][0]+val(0,i,n));
		for(int j=1;j<i;j++)
		{
			f[i][j]=inf;
			for(int k=0;k<j;k++)
				upd(f[i][j],f[j][k]+val(k,j,i));
			upd(ans,f[i][j]+val(j,i,n));
		}
	}
	printf("%lld\n",ans);
}
posted @ 2022-04-02 14:30  C202044zxy  阅读(192)  评论(1编辑  收藏  举报