[AGC036D] Negative Cycle
题意#
一张有向图,初始有边 ,边权为 。后来加入 条边,是对于每一对 ,连边 ,若 ,边权为 ,否则边权为 。
删去后来加入的每条边都需要一定的代价,删去 需要花费 ,求使得图中不存在负环。
Solution#
若直接考虑去掉负环,发现负环的可能太多了,于是考虑转化题意。考虑什么地方会用到负环。差分约束! 于是我们把题目中每一条连边逆向翻译成差分约束的描述:
- 若 ,则有:
- 若 ,则有:
相当于说我们要在后面两条中删去一些条件使得不等式组有解。首先根据第一条,这是一个递减的序列。这样的话,一旦我们保留了边 ,那么边 肯定是被保留的,因为 ,必然能够满足。同理,如果我们保留了边 ,那么边 必然被保留。我们只需要考虑从哪里开始保留。
接下来考虑如何解决这个问题。实际上,我们就是需要构造一组解,使得不等式满足这个解的花费最小。由于我们需要构造一个非严格下降序列,容易想到用 来维护。根据上面的分析,我们知道,对于一个位置 ,它向后的连边,当且仅当 需要删掉;向前的连边,当且仅当 需要删掉。于是令 表示前 个点,当前连续相等段的长度是 ,上一个连续相等段的长度是 ,所需要删去的最小花费。
这是可能有问题的,因为最终解不一定相邻两端中的数刚好是相差 的。但你仔细一想,这样肯定不优,因为如果我把它调整到相差 ,凭空会有向前的边可以不删。
转移需要分讨,若当前 ,则:
我们对 做前缀和优化上面的东西就可以做到 转移。
若当前 ,则:
虽然说可以对 前缀优化做到 转移,但是在 的每 个状态中才会有一个 ,所以暴力转移总复杂度还是 ,没有优化的必要。
可以滚动一维优化空间。
然后直接在 中找到最小值就是答案。
细节: 注意当且仅当 时 是合法的,不然总是存在上一段。而 是不能等于 的。建议对 特判。
Code#
// Problem:
// [AGC036D] Negative Cycle
//
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/AT_agc036_d
// Memory Limit: 1 MB
// Time Limit: 2000 ms
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb emplace_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define clr(a) memset(a,0,sizeof(a))
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
#define int long long
using namespace std;
const int MAXN=510;
int A[MAXN][MAXN],dp[2][MAXN][MAXN];
int A1[MAXN][MAXN],A2[MAXN][MAXN];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n;cin>>n;
rep(i,1,n) rep(j,1,n) if(i^j) cin>>A[i][j];
rep(i,1,n) rep(j,1,n) A1[i][j]=A1[i-1][j]+A[i][j];
rep(i,1,n) rep(j,1,n) A2[i][j]=A2[i][j-1]+A[i][j];
memset(dp[1],0x3f,sizeof(dp[1]));
dp[1][1][0]=0;
rep(i,2,n){
memset(dp[i&1],0x3f,sizeof(dp[i&1]));
rep(k,1,i-1) rep(l,1-(k==i-1),i-k)
dp[i&1][1][k]=min(dp[i&1][1][k],dp[(i-1)&1][k][l]+A2[i][i-k-1]);
rep(j,2,i) rep(k,1-(j==i),i-j)
dp[i&1][j][k]=dp[(i-1)&1][j-1][k]+A2[i][i-j-k]+A1[i-1][i]-A1[i-j][i];
}
int ans=INF;
rep(j,1,n) rep(k,1-(j==n),n-j)
ans=min(ans,dp[n&1][j][k]);
cout<<ans<<'\n';
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
2021-11-15 XJOI 2021.11.14 嗯哦癌批冲剌赛
2021-11-15 XJOI 2021.11.13 嗯哦癌批冲剌赛
2021-11-15 学习笔记——线段树合并