P4198 楼房重建
线段树维护以某点为开头的最长不下降子序列
使用下面calc函数能够计算线段树u维护的区间中,以x为开头最长不下降子序列的个数。
calc需要维护区间最值。
template<typename T>
int calc(int u,T x)
{if(tree[u].l==tree[u].r) return tree[u].v>x?1:0;if(tree[u<<1].v<=x) return calc(u<<1|1,x);return tree[u].cnt-tree[u<<1].cnt+calc(u<<1,x);
}
B-xay loves monotonicity
如果本题没有b数组,就是上面的楼房重建,用上面calc函数递归解决问题。
由于存在b数组的操作,首先是区间翻转可以懒标记解决。
对于贡献来说我们同样记录每个区间的最值,并且记录一下最值出现位置pos\text {pos}pos的bposb_{\text{pos}}bpos值,然后calc过程中记录一个pre即可实现递归。
注意引用的巧妙使用!!!
由于引用不难知道:
int calc(int u,int &mx,int &pre)
中的mx\text{mx}mx和pre\text{pre}pre始终表示当前考虑的子序列最后一个值和最后一个值的b
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
template <class T=int> T rd()
{T res=0;T fg=1;char ch=getchar();while(!isdigit(ch)) {if(ch=='-') fg=-1;ch=getchar();}while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();return res*fg;
}
const int N=200010;
int a[N],b[N],n,m;
struct node
{int l,r;int v,cnt;// 区间最值v 以左端点为起点的最长不下降子序列int bt,tag;// 区间最值位置的b值 懒标记
}tree[N<<2];
void pushdown(int u)
{if(!tree[u].tag) return;tree[u<<1].tag^=1;tree[u<<1|1].tag^=1;tree[u<<1].bt ^=1;tree[u<<1|1].bt ^=1;tree[u].tag=0;
}
int calc(int u,int &mx,int &pre)//在子树u中以mx为起点的贡献,pre为mx位置的b的值
{if(tree[u].v<mx) return 0;if(tree[u].l==tree[u].r){if(tree[u].v>=mx) // 满足不下降{int ans=(tree[u].bt!=pre); // 贡献需要和前一个b不一样mx=tree[u].v,pre=tree[u].bt;// 修改mx和prereturn ans;}return 0;}pushdown(u);if(tree[u<<1].v<mx) return calc(u<<1|1,mx,pre);int ans=calc(u<<1,mx,pre)+tree[u].cnt-tree[u<<1].cnt;mx=tree[u].v,pre=tree[u].bt;return ans;}
void pushup(int u)
{if(tree[u<<1].v>tree[u<<1|1].v)// 区间最值和最值出现位置的b(靠右边)tree[u].v=tree[u<<1].v,tree[u].bt=tree[u<<1].bt;elsetree[u].v=tree[u<<1|1].v,tree[u].bt=tree[u<<1|1].bt;int mx=tree[u<<1].v,pre=tree[u<<1].bt;tree[u].cnt=tree[u<<1].cnt+calc(u<<1|1,mx,pre);
}
void build(int u,int l,int r)
{tree[u]={l,r};if(l==r){tree[u].v=a[l];tree[u].bt=b[l];tree[u].cnt=1;return;}int mid=l+r>>1;build(u<<1,l,mid),build(u<<1|1,mid+1,r);pushup(u);
}
void change(int u,int pos,int v)
{if(tree[u].l==tree[u].r) return tree[u].v=v,void();pushdown(u);int mid=tree[u].l+tree[u].r>>1;if(pos<=mid) change(u<<1,pos,v);elsechange(u<<1|1,pos,v);pushup(u);
}
void filp(int u,int l,int r)
{if(l<=tree[u].l&&tree[u].r<=r){tree[u].bt^=1;tree[u].tag^=1;return;}pushdown(u);int mid=tree[u].l+tree[u].r>>1;if(l<=mid) filp(u<<1,l,r);if(r>mid)filp(u<<1|1,l,r);pushup(u);
}
int query(int u,int l,int r,int &mx,int &pre)
{if(l<=tree[u].l&&tree[u].r<=r) return calc(u,mx,pre);pushdown(u);int mid=tree[u].l+tree[u].r>>1;int v=0;if(l<=mid) v+=query(u<<1,l,r,mx,pre);if(r>mid)v+=query(u<<1|1,l,r,mx,pre);return v;
}
int main()
{n=rd();for(int i=1;i<=n;i++) a[i]=rd();for(int i=1;i<=n;i++) b[i]=rd();build(1,1,n);m=rd();while(m--){int op=rd(),t1=rd(),t2=rd();if(op==1) change(1,t1,t2);else if(op==2) filp(1,t1,t2);else{int mx=-1,pre=-1;// 先假计算上序列一个值的贡献printf("%d\n",query(1,t1,t2,mx,pre)-1);// -1表示删去计算上序列一个值的贡献}}return 0;
}