1. 程式人生 > >COCI. DIFERENCIJA(序列處理中的小技巧)

COCI. DIFERENCIJA(序列處理中的小技巧)

output long pla strong center tac 數值 pre put


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

題目大意:

給定一段序列,一段序列的權值是這段權值的最大值減去最小值,求每段序列的權值之和


做法:

Σ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; }
View Code


COCI. DIFERENCIJA(序列處理中的小技巧)