标签:std gif temp ext 代码 分析 src 复杂度 说明
题目大意:
给定一段序列,一段序列的权值是这段权值的最大值减去最小值,求每段序列的权值之和
做法:
Σimax-min 单调栈维护
分析:
可以知道最后答案是Σimax-min
那么可以分开求得Σimax和Σmin
如果我们有一个函数能求的序列的imax总和
我们可以把序列全部取反,最小值边最大值
可以同一个函数求得min总和
我们可以考虑一个值是多少区间的最大值
维护一个从大到小的单调栈
前面的可以直接求得 第一个比他大的值的位置last
last到i之间的序列就是a[i]为imax
我们再考虑他右边第一个比他大的位置怎么求
求得右边的长度,a[i]的个数就是左边*右边
因为是左边个数*右边个数
所以右边每加入一个 就会多一个左边个数
这里有个技巧就是我们记一个累加和now
表示我们记得是还没确定右边序列的点的左边序列长度之和
这样我们就不用一次次地加,我们加在一起累加,复杂度就为n
如果被弹出栈就减去对应的个数
这样每次加now 就把左边所以应该加的一次次的加上了
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<algorithm> using namespace std; const int N=3e5+12; int n; int a[N]; long long ans; int stack[N],temp; long long calc() { temp=1; long long now=0; long long sum=0; for(int i=1;i<=n;i++) { while(temp>1&&a[stack[temp]]<a[i]) //维护一个从大到小的栈 { now-=1LL*(stack[temp]-stack[temp-1])*a[stack[temp]];//他不能再最为最大值值,右边不能再扩展了 temp--; } now+=1LL*(i-stack[temp])*a[i];//如果前面一个点没有被影响说明它比这个大 他会一直累加 sum+=now;//相当于加了左边的所有可以为当前最大值的个数 stack[++temp]=i; } return sum; } int main() { freopen("a.in","r",stdin); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); ans+=calc(); for(int i=1;i<=n;i++) a[i]=-a[i]; ans+=calc(); printf("%lld\n",ans); return 0; }
标签:std gif temp ext 代码 分析 src 复杂度 说明
原文地址:https://www.cnblogs.com/Heey/p/9028881.html