JoyOI1473 校门外的数3 (线段树)

本文介绍了一种解决种树问题的有效算法。通过维护两种线段树(已种树与未种树),文章详细阐述了如何利用线段树进行区间更新与查询操作,以高效地解决种树种类数量的计算问题。

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

题意分析

题意很简单
一开始想用一个bitset维护,明显这个是超内存的。但是我为什么还要写

然后想了一下,能不能维护每次种树的左右区间来写,然而发现并不行。
接着想,维护种类和维护区间没办法了,能不能在种树的次数上下功夫呢?

我想维护一个种了树和没种树的线段树,询问就是查询区间内中了树的最大值和没种树的最小值,最后做一个差,这样的话样例是过的。写完交上去WA了,自己想了一个样例吧自己叉掉了。为什么我不提前想一下呢?

然后就想,肯定是维护种植的次数的关系。苦苦思考无果。看了看大佬的题解,思路泉涌思路如下:
还是思考种了树和没种树的关系,其实他们互为补集,这是非常明显的。
每次种树,所种树的种类也是不同的,这也是显而易见的,也就是如果当前种了n次树,那么现在街道上一定有n种树对吧?
好了,所以对于每一次查询,我们只需要知道,对于这段区间,有多少种树是重复的,然后做差就可以得到结果了。

所以问题就转换为,如何维护所种树是重复的。由于每次种树,不种树的区间被种树的区间分为了两段,所以这样维护左右两半部分比较方便,况且对于下面求解问题也是较为简便的。所以这里要分左右两部分计算。

下面举例说明算法的思想。
1. 街道长为[1,100],首先在[30,60]种树,那么左边没有种树的区间L[1,29],右边R[61,100],我们将这个区间分别加一(分别的意思是左右要用不同的线段树维护)。
2. 假如我们要查询[20,40]种树的信息。由于现在种树中了1次,所有tot=1。关键来了,想办法去重,重复的如何计算呢?在没有种树的左区间中查询右端点,在没有种树的右区间中查询左端点,也就是在L[1,29]中查询60,在R[61,100]中查询30,显然,均为0 ,所以结果就是tot-0-0 = 1
3. 大家可以举例子自己尝试,关键就是【在没有种树的左区间中查询右端点,在没有种树的右区间中查询左端点】,然后用种树的从次数减去即可

代码总览

#include<bits/stdc++.h>
using namespace std;
const int nmax =  60001;
const int INF = 0x3f3f3f3f;
typedef long long ll;
typedef struct{
    int l,r,ll,rr;
    int mid() { return ((l+r) >>1 );}
}Tree;
Tree tree[nmax<<2];
int hashback[nmax];
int n,m;
inline void pushdown(int rt){
    if(tree[rt].ll){
        tree[rt<<1].ll += tree[rt].ll;
        tree[rt<<1|1].ll += tree[rt].ll;
        tree[rt].ll  = 0;
    }
    if(tree[rt].rr){
        tree[rt<<1].rr += tree[rt].rr;
        tree[rt<<1|1].rr += tree[rt].rr;
        tree[rt].rr = 0;
    }
}
void build(int l, int r, int rt){
    tree[rt].l = l , tree[rt].r = r;
    tree[rt].ll = tree[rt].rr = 0;
    if(tree[rt].l == tree[rt].r){hashback[tree[rt].l] = rt; return;}
    build(l,tree[rt].mid(),rt<<1);
    build(tree[rt].mid() + 1 ,r, rt<<1|1);
}
void lupdate(int l, int r, int rt){
//    if(tree[rt].l >r || tree[rt].r < l) return;
    if(tree[rt].l >= l && tree[rt].r <= r){
        tree[rt].ll ++;
        return;
    }
//    if(tree[rt].l == tree[rt].r) return;
    if(tree[rt].l != tree[rt].r)  pushdown(rt);
    if(l<=tree[rt].mid()) lupdate(l,r,rt<<1);
    if(r>tree[rt].mid()) lupdate(l,r,rt<<1|1);
//    lupdate(l,r,rt<<1);
//    lupdate(l,r,rt<<1|1);
}
void rupdate(int l, int r, int rt){
//    if(tree[rt].l >r || tree[rt].r < l) return;
    if(tree[rt].l >= l && tree[rt].r <= r){
        tree[rt].rr ++;
        return;
    }
//    if(tree[rt].l == tree[rt].r) return;
    if(tree[rt].l != tree[rt].r) pushdown(rt);
    if(l<=tree[rt].mid()) rupdate(l,r,rt<<1);
    if(r>tree[rt].mid()) rupdate(l,r,rt<<1|1);
//    rupdate(l,r,rt<<1);
//    rupdate(l,r,rt<<1|1);
}
int lQuery(int pos, int rt){
    if(tree[rt].l == pos  && tree[rt].r == pos) return tree[rt].ll;
    if(tree[rt].l != tree[rt].r)  pushdown(rt);
    if(pos<= tree[rt].mid()) return lQuery(pos,rt<<1);
    if(pos > tree[rt].mid()) return lQuery(pos,rt<<1|1);
}
int rQuery(int pos , int rt){
    if(tree[rt].l == pos && tree[rt].r == pos) return tree[rt].rr;
    if(tree[rt].l != tree[rt].r)  pushdown(rt);
    if(pos<= tree[rt].mid())  return rQuery(pos,rt<<1);
    if(pos > tree[rt].mid())  return rQuery(pos,rt<<1|1);
}
int main(){
//    freopen("in.txt","r",stdin);
    scanf("%d %d",&n,&m);
    build(0,n,1);
    int k,a,b;
    int tot = 0;
    for(int i = 0;i<m;++i){
        scanf("%d %d %d",&k,&a,&b);
        if(k == 1){
            tot ++;
            lupdate(0,a-1,1);
            rupdate(b+1,n,1);
        }else{
            int tt = lQuery(b,1);
            int ttt = rQuery(a,1);
            printf("%d\n",tot - tt - ttt);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值