洛谷1950 長方形 (單調棧)
阿新 • • 發佈:2018-12-21
懶得放題目連結了
qwq
(果然我是菜的真實,單調棧都不會,gg)
首先我們看到這個題。
應該會想到就是直接列舉行,然後計算當前行的答案。
那現在,對於每一行來說,如果我們能夠維護出\(h[j]\)表示第\(j\)列的最近的一個不合法的位置。
那麼實際上就是求一堆矩形的並的一個圖形中。
有多少個矩形。
首先考慮暴力,我們可以直接列舉每一列,然後列舉他前面的列進行計算,這個複雜度是\(O(n^3)\)的。
那我們應該怎麼去優化他呢?
這時候就需要單調棧了!
我們用單調棧維護一個單調上升的序列。
通過列舉每個列,算以\((i,j)\)這個點為右下角的\(ans\)。
然後我們通過維護一個\(now\)
然後\(ans+=now\)
具體應該怎麼做呢?
對於每次加入,我們嘗試彈出棧裡面的元素,然後刪除他和他之前那個元素的位置之間的貢獻(也就是他們兩個之間的距離乘上棧頂元素的高度!)
(其實就相當於中間一段矩陣都是不合法的,沒法選)
最後加入的時候,記得加入當前列與棧頂之間的貢獻(就是當前列和棧頂元素之間的距離乘上當前列的\(i-h[j]\))
表示中間一段矩形都是可以算的。‘
這裡運用了一個很巧妙的思路就是每次只處理相鄰兩個元素的貢獻
具體實現還是看程式碼吧
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #include<vector> #include<map> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return x*f; } const int maxn = 1e6+1e2; struct Node { int pos,val; }; Node a[maxn]; int n,m; char s[3010][3010]; int h[maxn]; long long ans,now; int main() { n=read(),m=read(); for (int i=1;i<=n;i++) scanf("%s",s[i]+1); for (int i=1;i<=n;i++) { //if (s[i][j]=='*') h[j]=i; int top=0; now=0; for (int j=1;j<=m;j++) { if (s[i][j]=='*') h[j]=i; //維護每一列的最近的一個不合法點在哪裡 while (top>=1 && a[top].val<=h[j]) //之所以是>=,是因為h表示點的位置,越小反而越高 { now-=(a[top].pos-a[top-1].pos)*(i-h[a[top].pos]); //每次減去這一部分的答案 top--; } now+=(j-a[top].pos)*(i-h[j]);///計算當前元素和棧頂之間對ans的貢獻 a[++top].val=h[j]; a[top].pos=j; ans+=now; } } cout<<ans; return 0; }