Loading

题解:P9780 [HUSTFC 2023] Azur Lane

P9780 [HUSTFC 2023] Azur Lane

题意:

给定一长度为 \(m\) 的序列 \(a\),分别将序列 \(a\) 分成 \(1,2, \dots ,n\) 段(\(n\) 不确定,每段都不为空),要求每段内的数字单调不上升,定义代价为 \(\sum\limits_{i=1}^{k}cut[i]\)\(k\) 表示分成的段数,\(cut[i]\) 表示前 \(i\) 段内数字个数),输出分成 \(1,2, \dots ,n\) 段各自所需要的最小代价,若无解输出 \(-1\)

思路:

显然,\(1 \leq n \leq m\)

  • \(n=m\) 时,每个数字单独成段,代价恒为 \(\frac{(1+n) \times n}{2}\)(等差数列求和)。
  • \(n < m\) 时,由贪心得:一个数字越往后放越优(被重复计算的次数少)。那么考虑倒序枚举数字大小,若当前数字不小于后面数字,则将它加入后面数字的那一段使段数减小,并计算该段数代价,由贪心得,该代价为最优解。
  • 代码有注释,如果不理解可以手动模拟看下。

数据范围:

\(1 \leq m \leq 10^6\),求和会爆 int,要开 long long。

代码:

#include<bits/stdc++.h>
using namespace std;
long long m, k, a[1000005];
long long f[1000005]; //f[i] 表示分成 i 段时需要的代价 
int main(){
	ios::sync_with_stdio(0);
	cin >> m >> k;
	for(int i=1; i<=m; i++) cin >> a[i];
	f[m]=(1+m)*m/2; //当 n=m 时的代价 
	long long k=m; // k 表示当前的分段数量 
	for(int i=m-1; i>=1; i--){
		if(a[i]>=a[i+1]) {
			k--; //可以将 a[i] 与 a[i+1] 捆绑,因此分段数量减少 1 
			f[k]=f[k+1]-i; //减去该数字在上一个代价中的贡献 
		}
	}
	for(int i=1; i<=m; i++){
		if(!f[i]) cout << "-1 ";
		else cout << f[i] << " ";
	}
	return 0;
}
posted @ 2024-08-21 09:08  Anins  阅读(59)  评论(0)    收藏  举报  来源