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的
b
pos
b_{\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和pre
return 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;
else
tree[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);
else
change(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;
}