[题解] 春荔(cut) | 贪心

题目大意#

有一个长度为 n 的非负整数序列 ai,每次可以选择一段区间减去 1,要求选择的区间长度 [l,r],问最少多少次把每个位置减成 0

不保证有解,1lrn106, rl+1n2, 0ai109

解题思路#

首先由于每次是对一段区间操作,考虑先差分原序列得到 ci=aiai1(i[1,n+1])

先从费用流的角度考虑。

源往正权点连权值的流量,负权点往汇连权值的流量,一个可操作区间则表示为,将 i[i+l,i+r] 中的点连流量为 INF,费用为 1 的边。

跑最小费用最大流。没流满则无解,否则费用即要求的答案。

怎么优化呢,考虑到这题有个特殊性质  rl+1n2,也就是说,对于任意满足 j>i+r 的位置,也只需要 2 的费用即可从 ij1 的流量。

于是就贪心地考虑,对于正权点 i ,先对于只要 1 的费用的位置从后往前尽量流,然后再流费用为 2 的位置,如果有正权点或者负权点最后没有满流,就无解。

int main(){
	read(n), read(l), read(r); lfor(i, 1, n) read(a[i]);
	lfor(i, 1, n + 1) c[i] = a[i] - a[i - 1];
	rfor(i, n + 1, 1){
		if(c[i] < 0) Q.push_front(i);
		else if(c[i] > 0){
			while(!Q.empty() && c[i]){
				int x = Q.back(), det = min(c[i], -c[x]);
				c[i] -= det, c[x] += det, Ans += det;
				if(!c[x]) Q.pop_back();
			}
			if(c[i] > sum){ puts("-1"); return 0; }
			Ans += c[i] * 2, sum -= c[i], c[i] = 0;
		}
		while(!Q.empty() && i + r == Q.back()) sum -= c[Q.back()], Q.pop_back();
	}
	if(Q.size() || sum){ puts("-1"); return 0; }
	printf("%lld\n", Ans);
	return 0;
}
//  sto zhy12138 orz
posted @   IrisT  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
主题色彩