[CF 526 F] Pudding Monsters(单调栈 + 线段树)
CF526F Pudding Monsters
- problem
- solution
- code
problem
luogu翻譯
solution
observation :每行每列恰好有一個棋子,所以如果一段區間 [l,r][l,r][l,r] 會被某個 kkk 統計,當且僅當這個區間內棋子縱坐標 ymax?ymin+1=r?l+1y_{max}-y_{min}+1=r-l+1ymax??ymin?+1=r?l+1。
發現這是經典的連續段計數問題。
標配:單調棧 +++ 線段樹。
將所有棋子按照橫坐標升序排序。
枚舉右端點 rrr,線段樹上每個葉子節點 lll 維護的是 [l,r][l,r][l,r] 的信息。
轉換一下條件判定:ymax?ymin+l?r=0y_{max}-y_{min}+l-r=0ymax??ymin?+l?r=0。
因為每行每列恰好一個棋子,所以所有情況都有 ymax?ymin≥r?ly_{max}-y_{min}\ge r-lymax??ymin?≥r?l。
而最后要計入答案的是取等的情況,即最小值。
所以線段樹就維護 ymax?ymin+l?ry_{max}-y_{min}+l-rymax??ymin?+l?r 的最小值,以及最小值個數。
最后統計的時候,就統計最小值為 000 的節點信息。
-
rrr 每次都是 +1+1+1,對應的是線段樹整體 ?1-1?1。
-
lll 與 rrr 無關,在最開始建樹是加上即可。
-
ymaxy_{max}ymax?,維護一個單增棧,棧中第 toptoptop 個元素維護的是區間 [sMax[top?1]+1,sMax[top]]\Big[sMax[top-1]+1,sMax[top]\Big][sMax[top?1]+1,sMax[top]] 的最大值信息,即這一段的縱坐標最大值都是 sMax[top]sMax[top]sMax[top]。
每次用右端點 rrr 與棧比較縱坐標大小,如果 rrr 大則彈棧,將 toptoptop 維護的區間加上 yr?ysMax[top]y_r-y_{sMax[top]}yr??ysMax[top]?。
-
yminy_{min}ymin?,維護一個單減棧,棧中第 toptoptop 個元素維護的是區間 [sMin[top?1]+1,sMin[top]]\Big[sMin[top-1]+1,sMin[top]\Big][sMin[top?1]+1,sMin[top]] 的最大值信息,即這一段的縱坐標最小值都是 sMin[top]sMin[top]sMin[top]。
每次用右端點 rrr 與棧比較縱坐標大小,如果 rrr 小則彈棧,將 toptoptop 維護的區間加上 ysMin[top]?yiy_{sMin[top]-y_i}ysMin[top]?yi??。
事件復雜度為 O(nlog?n)O(n\log n)O(nlogn)。
code
#include <bits/stdc++.h> using namespace std; #define maxn 300005 int n, tMax, tMin; int x[maxn], y[maxn], id[maxn], sMax[maxn], sMin[maxn]; struct node { int tag, Min, cnt; }t[maxn << 2];#define lson now << 1 #define rson now << 1 | 1 #define mid ( ( l + r ) >> 1 )void pushup( int now ) {if( t[lson].Min < t[rson].Min ) t[now].Min = t[lson].Min, t[now].cnt = t[lson].cnt;else if( t[lson].Min > t[rson].Min ) t[now].Min = t[rson].Min, t[now].cnt = t[rson].cnt;else t[now].Min = t[lson].Min, t[now].cnt = t[lson].cnt + t[rson].cnt; }void pushdown( int now ) {if( t[now].tag ) {t[lson].tag += t[now].tag;t[rson].tag += t[now].tag;t[lson].Min += t[now].tag;t[rson].Min += t[now].tag;t[now].tag = 0;return;} }void build( int now, int l, int r ) {if( l == r ) { t[now].Min = l, t[now].cnt = 1; return; }build( lson, l, mid );build( rson, mid + 1, r );pushup( now ); }void modify( int now, int l, int r, int L, int R, int k ) {if( R < l or r < L ) return;if( L <= l and r <= R ) { t[now].Min += k, t[now].tag += k; return; }pushdown( now );modify( lson, l, mid, L, R, k );modify( rson, mid + 1, r, L, R, k );pushup( now ); }int query( int now, int l, int r, int L, int R ) { if( R < l or r < L ) return 0;if( L <= l and r <= R ) return t[now].Min ? 0 : t[now].cnt;return query( lson, l, mid, L, R ) + query( rson, mid + 1, r, L, R ); }int main() {scanf( "%d", &n );for( int i = 1;i <= n;i ++ ) scanf( "%d %d", &x[i], &y[i] ), id[i] = i;sort( id + 1, id + n + 1, []( int u, int v ) { return x[u] < x[v]; } );build( 1, 1, n );long long ans = 0;for( int i = 1;i <= n;i ++ ) {modify( 1, 1, n, 1, n, -1 );modify( 1, 1, n, x[sMax[tMax]] + 1, i, y[id[i]] );modify( 1, 1, n, x[sMin[tMin]] + 1, i, -y[id[i]] );while( tMax and y[sMax[tMax]] < y[id[i]] ) {modify( 1, 1, n, x[sMax[tMax - 1]] + 1, x[sMax[tMax]], y[id[i]] - y[sMax[tMax]] );tMax --;}while( tMin and y[sMin[tMin]] > y[id[i]] ) {modify( 1, 1, n, x[sMin[tMin - 1]] + 1, x[sMin[tMin]], y[sMin[tMin]] - y[id[i]] );tMin --;}sMax[++ tMax] = sMin[++ tMin] = id[i];ans += query( 1, 1, n, 1, i );}printf( "%lld\n", ans );return 0; }總結
以上是生活随笔為你收集整理的[CF 526 F] Pudding Monsters(单调栈 + 线段树)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [AtCoder Regular Con
- 下一篇: [luogu P4198] 楼房重建(线