【单调栈维护连续区间】2019.1.18模拟赛T2 浇花
这道题是一道单调栈的题
1 题目描述 2 JDFZ在餐厅门前种了一排nn棵花,每棵花都有一个高度。浇花大爷会枚举所有的区间,然后从区间中找出一个高度最矮的花进行浇水。由于浇花大爷浇完水之后就精疲力竭了,所以请你帮助他计算每棵花都被浇了几次水。 3 4 输入格式 5 第一行一个整数nn。 第二行nn个整数,分别表示每棵花的高度。 6 7 输出格式 8 一行nn个整数用空格隔开,分别表示每棵花被浇了几次水。 9 10 样例一 11 input 12 3 13 1 3 5 14 output 15 3 2 1 16 样例解释 17 浇花大爷枚举到了6个区间分别是[1], [3], [5], [1 3], [3 5], [1 3 5],对应的最矮的花的高度是1, 3, 5, 1, 3, 1。 18 限制与约定 19 对于40%的数据,n≤1000n≤1000 20 对于100%的数据,n≤1000000,保证每棵花的高度都不相同且≤231n≤1000000,保证每棵花的高度都不相同且≤231 21 时间限制:1s1s 22 空间限制:256MB
这道题能看出他必须要是连续的区间
首先是一个结论,就是假如一个数左面能达到n个数,右面能达到m个数,那么他的区间个数是(n+1)*(m+1)
给出证明
第一种
第一类当前数只和左面形成区间,区间数为m
第二类只和右面,同理方案数为n
第三类左右都和,方案数为nm
第四类自己就是一个区间,也就是1
加在一起,就是(n+1)*(m+1)得证
第二种
直接脑补,左面有n+1个位置可选,右面m+1个,乘在一起得证
那么现在问题就是怎么维护一个数他的左右都能达到哪里?
做法是单调栈
我们维护一个单调递减栈,
首先是右面能达到哪里,从前向后进栈,当这个数来了,从栈顶开始,把数一个个顶掉,这时被顶掉的的数的r值即为i-1,为什么呢,因为假如你在这个时候被后面顶掉了,说明就恰好这个时候后面的数比你小了,你的能力只够你达到这个数的前一个位置
然后左面的就很好理解了,从后往前进栈,然后l值变成i+1就ok
注意一点,就是最后在栈里,还是有一些数没有被顶掉,意味着这些数是可以坚持到最后的,把他们的值改成1或者n
代码在此
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 typedef long long ll; 6 int n; 7 int flw[1000000+100]; 8 int st1[1000000+100],st2[1000000+100]; 9 ll r[1000000+100],l[1000000+100]; 10 int top1,top2; 11 int main() 12 { 13 freopen("flw.in","r",stdin); 14 freopen("flw.out","w",stdout); 15 scanf("%d",&n); 16 for(int i=1;i<=n;i++) 17 scanf("%d",&flw[i]); 18 st1[++top1]=1; 19 for(int i=2;i<=n;i++) 20 { 21 while(flw[i]<flw[st1[top1]]&&top1) 22 r[st1[top1--]]=i-1; 23 st1[++top1]=i; 24 } 25 while(top1)r[st1[top1--]]=n; 26 st2[++top2]=n; 27 for(int i=n-1;i>=1;i--) 28 { 29 while(flw[i]<flw[st2[top2]]&&top2) 30 l[st2[top2--]]=i+1; 31 st2[++top2]=i; 32 } 33 while(top2)l[st2[top2--]]=1; 34 for(int i=1;i<=n;i++) 35 printf("%lld ",(r[i]-i+1)*(i-l[i]+1)); 36 fclose(stdin); 37 fclose(stdout); 38 return 0; 39 } 40 /* 41 5 42 3 1 2 5 4 43 */ 44 /* 45 对于40%的数据,n <= 1000 46 对于100%的数据,n <= 1000000,保证每棵花的高度都不相同 47 */