题目描述
春春幼儿园举办了一年一度的“积木大赛”。今年比赛的内容是搭建一座宽度为 nn 的大厦,大厦可以看成由 nn 块宽度为 11 的积木组成,第 ii 块积木的最终高度需要是 h_ihi。
在搭建开始之前,没有任何积木(可以看成 nn 块高度为 00 的积木)。接下来每次操作,小朋友们可以选择一段连续区间 [l, r][l,r],然后将第 LL 块到第 RR 块之间(含第 LL 块和第 RR 块)所有积木的高度分别增加 11。
小 M 是个聪明的小朋友,她很快想出了建造大厦的最佳策略,使得建造所需的操作次数最少。但她不是一个勤于动手的孩子,所以想请你帮忙实现这个策略,并求出最少的操作次数。
输入格式
包含两行,第一行包含一个整数 nn,表示大厦的宽度。
第二行包含 nn 个整数,第 ii 个整数为 h_ihi。
输出格式
建造所需的最少操作数。
输入输出样例
输入 #1复制
5 2 3 4 1 2
输出 #1复制
5
说明/提示
【样例解释】
其中一种可行的最佳方案,依次选择:[1,5][1,5],[1,3][1,3],[2,3][2,3],[3,3][3,3],[5,5][5,5]。
【数据范围】
- 对于 30\%30% 的数据,有 1 \leq n \leq 101≤n≤10;
- 对于 70\%70% 的数据,有 1 \leq n \leq 10001≤n≤1000;
- 对于 100\%100% 的数据,有 1 \leq n \leq 1000001≤n≤100000,0 \leq h_i \leq 100000≤hi≤10000。
一般区间操作首先要想差分————yql
差分是个啥?
△f=f(x+1)-f(x),就像微积分,但把增量换成了1,也叫有限微积分
这里定义差分f[i]=h[i]-h[i-1],i=1...n+1,h[0]=0,h[n+1]=0.
容易知道f中正值之和等于负值之和的绝对值
Σf[i]=Σh[i]-h[i-1]=h[n+1]-h[0]=0,得证.
于是我们可以猜ans=正值之和
证明?
如果f[i]为正,容易知道我们要往这一摞上搭积木,所以ans+=h[i]-h[i-1]=f[i]
如果f[i]为负,可以在之前搭的时候一起搭上去,所以ans不变
当然这可以通过其他方式省去数组,但差分有更多的用途
考虑区间加
在a(原数组,下同)上表现为i=l...r,a[i]+=w;
而在f数组上的表现则为f[l]+=w,f[r+1]-=w.
证明自己yy一下就好,很容易
于是这东西可以O(1)修改
可以推广到二维和树上,区间乘法应该也可以???适用于多询问少修改的题
然后今天有一道毒瘤题要求区间翻转+区间加+带负值积木大赛+查询历史版本
于是可以差分做
考虑翻转之后的f
对于l+1~r+1,可以取反然后翻转
对于l和r,暴力处理一下即可
丢到平衡树上就好了
历史版本加上一个操作树就好
没有强制在线就不需要持久化
到这是yql原话反正我没怎么听懂orz
最后回到这个题上来233
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int c[110000],n,ans=0;
inline int getin()
{
int x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();
return x;
}
inline int max(int a,int b)
{
if(a>b)return a;return b;
}
int main()
{
n=getin();
for(register int i=1;i<=n;i++)
c[i+1]=-getin(),c[i]-=c[i+1],ans+=max(c[i],0);
printf("%d",ans);
}
4ms...慢的要死,可以自己再卡卡常数
区间的话分块O(nsqrt(n))/线段树(nlogn)啥的都能做
我这么菜不会别的233