题解 P5019 【铺设道路】

这题目就我一个人模拟吗?

普及蒟蒻,不会好算法,就来说一发模拟吧

首先一个贪心:

每次选一个连续正深度的坑的区间去填

为什么呢?因为只有这样,才能保证我每次填坑的数量最多,不会造成浪费(即可以一天解决的问题我2天解决),也就是保证填的天数最少

于是得到了$ O(n^2* sum(a[1…n])) $,嗯,超时成什么样我就不说了

于是优化:

观察下面的表:

d[1] d[2] d[3] d[4] d[5] d[6] d[7]
1 4 3 3 4 3 3
0 3 2 2 3 2 2
0 2 1 1 2 1 1
0 1 0 0 1 0 1
0 0 0 0 0 0 0

可以发现,第2行与第3行实际上是重复的

在本例中,只重了2行;

如果重个10000,100000行呢?

那么你的程序就会T。

如何优化?

不难发现,我们填坑的过程是有规律的;

于是就有了优化:

每次循环必然要彻底填掉至少1个坑

那么,实现就很简单了:

在找连续整数的时候,顺便查找最小值,然后区间减最小值,完成目标。

到此为止,你已经拿到80分了

还有两个点什么情况?

因为,尽管我们优化了,整个复杂度还是$ O(n^{2}) $,是会T的

再优化!

我们可以发现,当我们找区间的开始的时候,其实是有单调性的

即:我下一次填坑的起始点一定在本次填坑的范围中

那么在模拟减的时候就可以顺便找下一次的开始了。

你也许发现了一个问题:

一遍坑全部填平了怎么办?

没事,设定一个极值,最后判一下,如果没变的话就从本次的结束点往后找啦~

于是复杂度降到了$ O(n+\text{常数}) $,就AC了

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <bits/stdc++.h>
using namespace std;
int n,d[100005],q=1;
long long s,ans;
int main(){
scanf ("%d",&n);
for (int i=1;i<=n;i++)scanf ("%d",&d[i]),s+=d[i];//读入,统计和
int b=1;
while (d[b]==0)b++;//找开端
q=b;//下一个开端
while (s){
b=q;
int e=b,mn=1<<20;
while (d[e])mn=min(mn,d[e]),e++;//找最小值
ans+=mn,s-=mn*(e-b);//花mn天干,总和减去填的所有
q=1<<20;
for (int i=e-1;i>=b;i--){d[i]-=mn;if (d[i]>0)q=i;}//模拟减,同时找下一次的开端
if (q==1<<20)for (q=e+1;q<=n;q++)if(d[q]>0)break;
}
printf ("%d",ans);
}