COCI. DIFERENCIJA(序列处理中的小技巧)
Posted heey
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了COCI. DIFERENCIJA(序列处理中的小技巧)相关的知识,希望对你有一定的参考价值。
DIFERENCIJA
题目类型:虚拟题目 时间限制:1.0s 空间限制:64.0MB 提交文件大小限制:100.0KB 提示:%I64d & %I64u
抓题完成
序列的值被定义成其中最大的元素和最小的元素的中间的数的个数。如序列(3, 1, 7, 2) 的值为6, 序列(42, 42)的值为0.现给定一个序列,要求出所有连续子序列的值的和。
输入:
第一行一个整数 N (2 ≤ N ≤ 300 000), 表示序列的元素个数。
接下来N行,每行一个正整数值不超过 100000 000.
输出:
一个数,表示所有子序列的值的和
EXAMPLE TEST DATA
input
3
1
2
3
output
4
输入:
第一行一个整数 N (2 ≤ N ≤ 300 000), 表示序列的元素个数。
接下来N行,每行一个正整数值不超过 100000 000.
输出:
一个数,表示所有子序列的值的和
EXAMPLE TEST DATA
input
3
1
2
3
output
4
题目大意:
给定一段序列,一段序列的权值是这段权值的最大值减去最小值,求每段序列的权值之和
做法:
Σ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; }
以上是关于COCI. DIFERENCIJA(序列处理中的小技巧)的主要内容,如果未能解决你的问题,请参考以下文章