当前位置:网站首页>P5019 [NOIP2018 提高组] 铺设道路
P5019 [NOIP2018 提高组] 铺设道路
2022-07-31 12:49:00 【WDLieqi】
[NOIP2018 提高组] 铺设道路
题目背景
NOIP2018 提高组 D1T1
题目描述
春春是一名道路工程师,负责铺设一条长度为 n n n 的道路。
铺设道路的主要工作是填平下陷的地表。整段道路可以看作是 n n n 块首尾相连的区域,一开始,第 i i i 块区域下陷的深度为 d i d_i di 。
春春每天可以选择一段连续区间 [ L , R ] [L,R] [L,R] ,填充这段区间中的每块区域,让其下陷深度减少 1 1 1。在选择区间时,需要保证,区间内的每块区域在填充前下陷深度均不为 0 0 0 。
春春希望你能帮他设计一种方案,可以在最短的时间内将整段道路的下陷深度都变为 0 0 0 。
输入格式
输入文件包含两行,第一行包含一个整数 n n n,表示道路的长度。 第二行包含 n n n 个整数,相邻两数间用一个空格隔开,第 i i i 个整数为 d i d_i di 。
输出格式
输出文件仅包含一个整数,即最少需要多少天才能完成任务。
样例 #1
样例输入 #1
6
4 3 2 5 3 5
样例输出 #1
9
提示
【样例解释】
一种可行的最佳方案是,依次选择:
[ 1 , 6 ] [1,6] [1,6]、 [ 1 , 6 ] [1,6] [1,6]、 [ 1 , 2 ] [1,2] [1,2]、 [ 1 , 1 ] [1,1] [1,1]、 [ 4 , 6 ] [4,6] [4,6]、 [ 4 , 4 ] [4,4] [4,4]、 [ 4 , 4 ] [4,4] [4,4]、 [ 6 , 6 ] [6,6] [6,6]、 [ 6 , 6 ] [6,6] [6,6]。
【数据规模与约定】
对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 10 1 ≤ n ≤ 10 1≤n≤10 ;
对于 70 % 70\% 70% 的数据, 1 ≤ n ≤ 1000 1 ≤ n ≤ 1000 1≤n≤1000 ;
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 100000 , 0 ≤ d i ≤ 10000 1 ≤ n ≤ 100000 , 0 ≤ d_i ≤ 10000 1≤n≤100000,0≤di≤10000 。
思路
这道题的正规思路是用贪心的办法来进行求解,复杂度位O(n),但这里我的解题方法是线段树,复杂度O(nlogn),正规解法在原题里面的题解可以看到。
正文来了
因为我们可以进行操作的前提条件是连续区间内不能有等于零的数,所以选的区间里面必须要全部大于零。通过观察我们很容易的发现将最小的数减为零后可以将原区间分为左右两个区间例如:
45233 4 5 2 3 3 45233 变为 23011 2 3 0 1 1 23011 然后就可变为 23 2 3 23 和 11 1 1 11
所以通过不断划分区间来让全部的数变成 0
接下来是线段树维护的信息
我们要完成上述的操作就要知道两个信息,最小值和最小值的下标,所以我们的线段树记录的就是最小值和最小值的下标。
代码
#include <bits/stdc++.h>
using namespace std;
#define IOS \ ios::sync_with_stdio(false); \ cin.tie(0); \ cout.tie(0);
#define P acos(-1);
typedef long long LL;
typedef unsigned long long ULL;
#define PII pair<int, int>
#define PDD pair<double, double>
#define Pll pair<LL, LL>
typedef unsigned long long ULL;
const double eps = 1e-7;
const int N = 1e5 + 10, M = 1e6 + 10, mod = 998244353;
const int inf = 0x3f3f3f3f;
int dx[] = {
1, -1, 0, 0, -1, 1, 1, -1}, dy[] = {
0, 0, -1, 1, -1, -1, 1, 1};
int lowbit(int x) {
return (x & -x); };
struct node
{
int l, r;
int minn, add, pos;
}tr[N * 4];
int a[N];
int n, sum;
queue<PII> q;
void pushup(int u)
{
if (tr[u << 1].minn < tr[u << 1 | 1].minn)
{
tr[u].minn = tr[u << 1].minn;
tr[u].pos = tr[u << 1].pos;
}
else
{
tr[u].minn = tr[u << 1 | 1].minn;
tr[u].pos = tr[u << 1 | 1].pos;
}
}
void pushdown(int u)
{
auto &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
if (root.add)
{
left.add += root.add, left.minn -= root.add;
right.add += root.add, right.minn -= root.add;
root.add = 0;
}
}
void build(int u, int l, int r)
{
if (l == r) tr[u] = {
l, r, a[l], 0, l};
else
{
tr[u] = {
l, r};
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void modify(int u, int l, int r, int d)
{
if (tr[u].l >= l && tr[u].r <= r)
{
tr[u].minn -= d;
tr[u].add += d;
return;
}
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
if (mid >= l) modify(u << 1, l, r, d);
if (mid < r) modify(u << 1 | 1, l, r, d);
pushup(u);
}
node query(int u, int l, int r)
{
if (tr[u].l >= l && tr[u].r <= r) return tr[u];
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
node ans1, ans2;
if (mid >= r) return query(u << 1, l, r);
else if (mid < l) return query(u << 1 | 1, l, r);
ans1 = query(u << 1, l, r), ans2 = query(u << 1 | 1, l, r);
if (ans1.minn < ans2.minn) return ans1;
return ans2;
}
void solve()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
build(1, 1, n);
q.push({
1, n});
node ans;
while (q.size())
{
int f = q.front().first, g = q.front().second;
q.pop();
ans = query(1, f, g);
sum += ans.minn;
if (ans.minn) modify(1, f, g, ans.minn);
if (ans.pos > f) q.push({
f, ans.pos - 1});
if (ans.pos < g) q.push({
ans.pos + 1, g});
}
printf("%d", sum);
}
int main()
{
int T = 1;
//scanf("%d", &T);
while (T -- )
{
solve();
}
return 0;
}
O(n) 复杂度正规解法
大坑带小坑,小坑会被大坑带着填满。
大坑会减少两者的差,相当于这部分是免费的
#include <bits/stdc++.h>
using namespace std;
#define IOS \ ios::sync_with_stdio(false); \ cin.tie(0); \ cout.tie(0);
#define P acos(-1);
typedef long long LL;
typedef unsigned long long ULL;
#define PII pair<int, int>
#define PDD pair<double, double>
#define Pll pair<LL, LL>
typedef unsigned long long ULL;
const double eps = 1e-7;
const int N = 1e5 + 10, M = 1e6 + 10, mod = 998244353;
const int inf = 0x3f3f3f3f;
int dx[] = {
1, -1, 0, 0, -1, 1, 1, -1}, dy[] = {
0, 0, -1, 1, -1, -1, 1, 1};
int lowbit(int x) {
return (x & -x); };
int n, a[N];
LL ans = 0;
void solve()
{
cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
for (int i = 2; i <= n; i ++ )
if (a[i] > a[i - 1]) ans += a[i] - a[i - 1];
cout << ans + a[1];
}
int main()
{
int T = 1;
// scanf("%d", &T);
while (T--)
{
solve();
}
return 0;
}
边栏推荐
猜你喜欢
随机推荐
PyQt5快速开发与实战 9.7 UI层的自动化测试
Spark GC日志分析
PyQt5 rapid development and actual combat 10.1 Get city weather forecast
电商rpa是什么意思?跟电商rpi是一个意思吗?
Markdown编辑器语法
go中select语句
vivado里那些看不懂的原语
kernel syscore
Using SQL Server FOR XML and FOR JSON syntax on other RDBMSs with jOOQ
Error EPERM operation not permitted, mkdir ‘Dsoftwarenodejsnode_cache_cacach两种解决办法
golang中使用泛型
硬盘分区,拓展C盘,不重装系统,不重装D盘软件的全教程。
函数的参数
Getting started with jmeter performance testing steps (performance testing tool jmeter)
centos7安装mysql5.7
字符函数和字符串函数
Use IN List Population in Your JDBC Application to Avoid Cursor Cache Contention Issues
SAP 电商云 Spartacus SSR Optimization Engine 几处 timeout 的执行顺序
ASM外部冗余是否可以替换磁盘
基于生物激励神经网络的室内实时激光SLAM控制方法









