当前位置:网站首页>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;
}
边栏推荐
猜你喜欢
随机推荐
Using SQL Server FOR XML and FOR JSON syntax on other RDBMSs with jOOQ
PyQt5快速开发与实战 10.1 获取城市天气预报
SAP 电商云 Spartacus SSR Optimization Engine 几处 timeout 的执行顺序
Error EPERM operation not permitted, mkdir ‘Dsoftwarenodejsnode_cache_cacach两种解决办法
Structural controllability of switched linear systems with symmetry constraints
五种数据提交方式的优化
Hybrid brain-computer interface system based on steady-state visual evoked potentials and attentional EEG
消息队列面试题(2022最新整理)
查看Mysql数据库版本
2022年最新重庆建筑安全员模拟题库及答案
跨境电商小知识之跨境电商物流定义以及方式讲解
Selenium自动化测试之Selenium IDE
PyQt5快速开发与实战 9.7 UI层的自动化测试
通过斐波那契数再谈函数递归2.0
使用openssl命令生成证书和对应的私钥,私钥签名,公钥验签
NameNode故障处理的两种方法
imx6ull看门狗使用
Flutter键盘可见性
SAP message TK 248 solved
双非一本进字节了!!纯干货分享