【经典问题】#176. 栈
经典问题:线段树维护单调栈;前置技能:bzoj2957: 楼房重建
题目大意
维护$n$个单调栈,要求支持区间加数和单点查询。
$n \le 2\times 10^5$
题目分析
首先考虑单调栈这个问题应该如何转化:将入栈的所有数按加入的时间顺序看成一个序列,得到下图
那么对于每个时刻$t$的查询,答案就是以$t$为右端点的上升子序列的权值和(注意不是长度)。
于是单点的子问题就转为经典问题,参见bzoj2957.
剩下的对于这个序列问题的处理相当套路,由于是区间修改单点查询,并且对于每个点的查询是依靠存在元素的独立询问,那么就先离线对插入序列差分,即在$l$修改$t$时刻的$c$、在$r+1$时刻修改$t$时刻成$0$;剩下对每个栈处理所有询问。这里把$t$时刻处理成0意味着后续时刻的查询就是忽视这个时刻的元素。
总结一下,对于区间修改单点询问的序列问题,就应该先从单点询问的角度入手,再从离线保留时间序列公用元素的方式降维考虑问题。
(为什么我这么慢)
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 const int maxn = 200035; 4 5 struct node 6 { 7 ll mx,val; 8 }f[maxn<<2]; 9 struct point 10 { 11 int t,d; 12 point(int a=0, int b=0):t(a),d(b) {} 13 }; 14 int n,m; 15 ll ans[maxn]; 16 std::vector<int> qr[maxn]; 17 std::vector<point> opt[maxn]; 18 19 int read() 20 { 21 char ch = getchar(); 22 int num = 0, fl = 1; 23 for (; !isdigit(ch); ch=getchar()) 24 if (ch=='-') fl = -1; 25 for (; isdigit(ch); ch=getchar()) 26 num = (num<<1)+(num<<3)+ch-48; 27 return num*fl; 28 } 29 ll calc(int rt, int c, int l, int r) 30 { 31 if (f[rt].mx <= c) return 0; 32 if (l==r) return (f[rt].mx > c)?f[rt].mx:0; 33 int mid = (l+r)>>1; 34 if (f[rt<<1|1].mx <= c) return calc(rt<<1, c, l, mid); 35 return f[rt].val-f[rt<<1|1].val+calc(rt<<1|1, c, mid+1, r); 36 } 37 void modify(int rt, int l, int r, int pos, int c) 38 { 39 if (l==r) f[rt].mx = f[rt].val = c; 40 else{ 41 int mid = (l+r)>>1; 42 if (pos <= mid) modify(rt<<1, l, mid, pos, c); 43 if (pos > mid) modify(rt<<1|1, mid+1, r, pos, c); 44 f[rt].mx = std::max(f[rt<<1].mx, f[rt<<1|1].mx); 45 f[rt].val = f[rt<<1|1].val+calc(rt<<1, f[rt<<1|1].mx, l, mid); 46 } 47 } 48 int queryMax(int rt, int L, int R, int l, int r) 49 { 50 if (L > R) return 0; 51 if (L <= l&&r <= R) return f[rt].mx; 52 int mid = (l+r)>>1, ret = 0; 53 if (L <= mid) ret = queryMax(rt<<1, L, R, l, mid); 54 if (R > mid) ret = std::max(ret, queryMax(rt<<1|1, L, R, mid+1, r)); 55 return ret; 56 } 57 ll query(int rt, int l, int r, int c) 58 { 59 if (r <= c) return calc(rt, queryMax(1, r+1, c, 1, m), l, r); 60 int mid = (l+r)>>1; 61 if (c <= mid) return query(rt<<1, l, mid, c); 62 return query(rt<<1, l, mid, c)+query(rt<<1|1, mid+1, r, c); 63 } 64 int main() 65 { 66 memset(ans, -1, sizeof ans); 67 n = read(), m = read(); 68 for (int i=1; i<=m; i++) 69 { 70 int opd = read(); 71 if (opd==1){ 72 int l = read(), r = read(), x = read(); 73 opt[l].push_back(point(i, x)); 74 opt[r+1].push_back(point(i, 0)); 75 }else qr[read()].push_back(i); 76 } 77 for (int i=1; i<=n; i++) 78 { 79 for (int j=0; j<(int)opt[i].size(); j++) 80 modify(1, 1, m, opt[i][j].t, opt[i][j].d); 81 for (int j=0; j<(int)qr[i].size(); j++) ans[qr[i][j]] = query(1, 1, m, qr[i][j]); 82 } 83 for (int i=1; i<=m; i++) 84 if (ans[i]!=-1) printf("%lld\n",ans[i]); 85 return 0; 86 }
END