【題解】CF601B Lipshitz Sequence
阿新 • • 發佈:2021-06-25
這種題第一眼看上去不可做,我們考慮證明一個結論:
對於一個區間的 \(Lipschitz\) 常數 \(k\) ,應滿足:
\[k=\max_{i=l}^{r-1} \left\{\frac{a[i+1]-a[i]}{i+1-i}\right\} \]即
\[k=\max_{i=l}^{r-1} \left\{|a[i+1]-a[i]|\right\} \]證明:
考慮三個數的情況,設它們分別是:\(\left\{0,x,\infty\right\}\)
那麼,相鄰的區間的 \(Lipschitz\) 常數 \(k=\left\{x,\infty-x\right\}\)
而整體區間的 \(Lipschitz\)
那麼考慮 \(x\) 的取值會發現,無論 \(x\) 怎麼取值,相鄰區間的 \(Lipschitz\) 常數 \(k\) 必然要大於整體區間的 \(k.\) 而根據三個數的區間進行推廣即可。
證畢。
那麼我們接下來的任務就是回答詢問了,觀察到詢問數目很少,所以我們用 \(O(nq)\) 的複雜度就可以了。
考慮如何 \(O(n)\) 回答一組詢問:
觀察相鄰區間的 \(k\) 會出現多少次。我們求出左邊和右邊第一個大於它的數的位置,那麼在這個區間中,任意一個包含它的區間都會讓 \(k\) 出現一次。那麼如果我們可以 \(O(n)\) 求出這個東西,問題就解決了。
顯然單調棧可以完成這個任務。
於是這題做完了。單調棧內部維護數值遞減的序列下標,每次彈出棧的時候更新棧頂元素的目標值就好了。
總複雜度:\(O(nq).\)
#include<bits/stdc++.h> using namespace std; #define int long long const int MAXN=2e5+10; const int inf=(1<<30); int n,q,a[MAXN],h[MAXN]; inline int Abs(int x){if(x<0)return -x;return x;} int st[MAXN],top; int l[MAXN],r[MAXN]; signed main(){ scanf("%lld%lld",&n,&q); for(int i=1;i<=n;++i)scanf("%lld",&h[i]); for(int i=1;i<n;++i)a[i]=Abs(h[i+1]-h[i]); for(;q;q--){ int L,R; scanf("%lld%lld",&L,&R); R--; memset(l,0,sizeof l); memset(r,0,sizeof r); int ans=0; for(int i=L,j=1;i<=R;++i,++j)a[j]=Abs(h[i+1]-h[i]); int len=R-L+1; top=0; for(int i=1;i<=len;++i){ while(top&&a[st[top]]<a[i]){ r[st[top]]=i-st[top]; top--; } l[i]=i-st[top]; st[++top]=i; } while(top)r[st[top]]=len+1-st[top],top--; for(int i=1;i<=len;++i)ans+=a[i]*l[i]*r[i]; printf("%lld\n",ans); } return 0; }