题目链接

有一排长为\(n\)的栅栏,你可以将一段长为\(k\)的栅栏染色,但高度不能超过最矮的那根.问最小刷不到的面积,以及在此前提下的染色次数

单调队列,贪心


分析:

既然问最小刷不到的面积,我们可以考虑极限情况,即把每一段长为\(k\)的栅栏都刷一遍,如果这样都刷不到那就没法了

我们定义\(mi[i]\)表示刷子的左端点在\(i\),能够往上刷的最远距离,设原数组为\(val\)

显然\(mi[i] = min\{val[j] \quad | \quad i \leq n - k + 1,i \leq j \leq i + k - 1\}\)

因为刷子必须完全接触栅栏,所以刷子左端点有限制

我们再设\(mx[i]\)表示\(i\)这根栅栏在极限情况下可以往上刷的最远距离

\(mx[i] = max\{mi[j] \quad | \quad max(1,i - k + 1) \leq j \leq i\}\)

那么最小刷不到的面积$ans1 = \sum_{i =1}^{n}{val[i] -mx[i]} $

以上都可以用单调队列在\(O(n)\)的时间内处理出来

对于最少刷不到的次数,我们可以考虑贪心处理,详见代码

#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 100;
int val[maxn],q[maxn],mi[maxn],mx[maxn],n,k;
ll ans1,ans2;//注意开long long
inline void init(){//单调队列预处理
	int head = 0,tail = 0;
	for(int i = 1;i < k;i++){
		while(head <= tail && val[q[tail]] >= val[i])tail--;
		q[++tail] = i;
	}
	for(int i = k;i <= n;i++){
		while(head <= tail && val[q[tail]] >= val[i])tail--;
		q[++tail] = i;
		while(head <= tail && q[head] <= i - k)head++;
		mi[i - k + 1] = val[q[head]];
	}
	head = 0,tail = 0;
	for(int i = 1;i < k;i++){
		while(head <= tail && mi[q[tail]] <= mi[i])tail--;
		q[++tail] = i;
		mx[i] = mi[q[head]];
		ans1 -= mx[i];
	}
	for(int i = k;i <= n;i++){
		while(head <= tail && mi[q[tail]] <= mi[i])tail--;
		q[++tail] = i;
		while(head <= tail && q[head] <= i - k)head++;
		mx[i] = mi[q[head]];
		ans1 -= mx[i];
	}
}
int main(){
	scanf("%d %d",&n,&k);
	for(int i = 1;i <= n;i++)scanf("%d",val + i),ans1 += val[i];
	init();
	printf("%lld\n",ans1);
	int v = -1,rt = -1;//v表示当前往上刷的最远距离,rt表示这段的右端点
	for(int i = 1;i <= n;i++)
		if(mx[i] != v || rt < i){//如果刷不完i这个位置或者刷不到i这个位置
			ans2++;
			v = mx[i];
			rt = i + k - 1;
		}
	printf("%lld\n",ans2);
	return 0;
}

相关文章: