CF526F:Pudding Monsters 题解

F:

题意:输入排列p,求有多少长度为 L 的区间满足 maxmin+1=L . (n<=1e5)


Solution:

一道比较经典的数据结构题。

由于是排列,因此需要想到:枚举数字和枚举位置的思路其实是一样的。事实上原题面是二维平面每行每列都有一个点,求多少边长为 L 的方框内有 L 个点。

那么我们枚举右端点 r,求有多少个左端点 l 满足题意。

我们用线段树维护 max[l,r]min[l,r]L[l,r]+1,这是个比较有启发的做法,虽然维护的内容比较奇特,但是易于区间更新。

马上我们就会发现 max,min,L 都是可以更新的。

更新 L 最简单,每次 r 向右枚举一位,所有的区间长度加一,即线段树区间加。

更新 max,min 原理相同,这两个值在固定右端点时向左都是单调的,可以用单调栈来维护,每次弹出一个数或压入一个数都可以用线段树区间加的形式来更新。为了避免同一数字重复出入栈,我们记录每个数字的数值以及出现了几次(同样方法参考CF1905D题解)。

更新是区间加,那么我们具体维护的是什么呢,维护的是最小值以及最小值出现次数。因为你会发现所有合法的情况满足 maxminL+1=0 ,而且不可能有负数出现,所以每次询问线段树最小值,如果是0答案加上出现次数(事实上由于l=r的情况存在,最小值肯定是0)。

数据结构的代码是基本功,没有特殊点。

ll T; ll n; ll a[N]; struct E{ ll l,r,zhi; }da[N],xi[N]; ll cnt1,cnt2; struct D{ ll mi,num; }t[N<<2],ling; ll tag[N<<2]; inline D pushup(D A,D B) { D C = ling; if(A.mi==B.mi) C.num = A.num + B.num; else if(A.mi<B.mi) C.num = A.num; else if(A.mi>B.mi) C.num = B.num; C.mi = min(A.mi, B.mi); return C; } inline void pushdown(ll now) { t[ls].mi += tag[now]; t[rs].mi += tag[now]; tag[ls] += tag[now]; tag[rs] += tag[now]; tag[now] = 0; } void built(ll now,ll l,ll r) { if(l==r) { t[now].mi = 0; t[now].num = 1; return ; } ll mid = l+r >> 1; built(ls, l, mid); built(rs, mid+1, r); t[now] = pushup(t[ls], t[rs]); } void insert(ll now,ll l,ll r,ll x,ll y,ll g) { if(x<=l && r<=y) { t[now].mi += g; tag[now] += g; return ; } pushdown(now); ll mid = l+r >> 1; if(x<=mid) insert(ls, l, mid, x, y, g); if(y>mid) insert(rs, mid+1, r, x, y, g); t[now] = pushup(t[ls], t[rs]); } D query(ll now,ll l,ll r,ll x,ll y) { if(x<=l && r<=y) return t[now]; pushdown(now); ll mid = l+r >> 1; D A = ling, B = ling; if(x<=mid) A = query(ls, l, mid, x, y); if(y>mid) B = query(rs, mid+1, r, x, y); return pushup(A,B); } int main() { n = read(); da[0] = {0,0,inf}; xi[0] = {0,0,-inf}; built(1,1,n); for(ll i=1;i<=n;i++) a[read()] = read(); ll lst = 0, ans = 0; for(ll i=1;i<=n;i++) { while(cnt1 && da[cnt1].zhi<a[i]) insert(1, 1, n, da[cnt1].l, da[cnt1].r, -da[cnt1].zhi), cnt1--; lst = da[cnt1].r; insert(1, 1, n, lst+1, i, a[i]); da[++cnt1] = {lst+1,i,a[i]}; while(cnt2 && xi[cnt2].zhi>a[i]) insert(1, 1, n, xi[cnt2].l, xi[cnt2].r, xi[cnt2].zhi), cnt2--; lst = xi[cnt2].r; insert(1, 1, n, lst+1, i, -a[i]); xi[++cnt2] = {lst+1,i,a[i]}; insert(1, 1, n, 1, i, -1); ans += query(1, 1, n, 1, i).num; } cout<<ans; return 0; }

__EOF__

本文作者枫叶晴
本文链接https://www.cnblogs.com/maple276/p/18037445.html
关于博主:菜菜菜
版权声明:呃呃呃
声援博主:呐呐呐
posted @   maple276  阅读(44)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示