2021牛客暑期多校训练营7 B-xay loves monotonicity(线段树+不降子序列)

本文介绍了一种通过线段树数据结构和b数组优化的方法,处理楼房重建问题,计算以特定点开始的最长不下降子序列。文章详细讲解了如何利用calc函数进行递归计算,以及如何通过区间翻转和懒标记处理b数组的影响。重点在于引用的巧妙使用和区间维护的最值更新策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值