zoukankan      html  css  js  c++  java
  • [题解] 春荔 | 贪心

    题目大意

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

    不保证有解,\(1 \leq l \leq r \leq n \leq 10^6,\ r - l + 1 \geq \lceil \frac{n}{2} \rceil,\ 0 \leq a_i \leq 10^9\)

    解题思路

    首先由于每次是对一段区间操作,考虑先差分原序列得到 \(c_i=a_i-a_{i-1}(i\in[1,n+1])\)

    先从费用流的角度考虑。

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

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

    怎么优化呢,考虑到这题有个特殊性质 \(\ r - l + 1 \geq \lceil \frac{n}{2} \rceil\),也就是说,对于任意满足 \(j>i+r\) 的位置,也只需要 \(2\) 的费用即可从 \(i\)\(j\)\(1\) 的流量。

    于是就贪心地考虑,对于正权点 \(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
    
  • 相关阅读:
    切换到真正的最高权限 SYSTEM 账户界面
    javascript中replace的正则表达式语法
    微软系统漏洞_超长文件路径打造私人地盘
    JAVA控制台
    PowerPoint绘图笔不能用
    《JavaScript核心技术》
    Catch(...) C++中三个点
    XMLHttp连续调用SEND需要注意的问题
    Wscript中的事件机制
    JavaScript(JS)常用的正则表达式
  • 原文地址:https://www.cnblogs.com/IrisT/p/15539751.html
Copyright © 2011-2022 走看看