线段树解决区间计算问题

问题一、

POJ3264

在这里插入图片描述
在这里插入图片描述
这一题只涉及了查询线段树的最大值&最小值

Code

#include <iostream>
#include <utility>
#define lc (p<<1)
#define rc (p<<1|1)
using namespace std;
const int N = 5e4+10;
struct Node{
    int l,r;
    int maxn;
    int minn;
}tree[N<<2];
int INF = (1<<29);
typedef pair<int,int> pii;
int a[N];
void build(int p,int l,int r){
    tree[p].l = l;
    tree[p].r = r;
    tree[p].maxn = 0;
    tree[p].minn = INF;
    if(l==r){
       tree[p].minn = tree[p].maxn = a[l];
       return;
    }
    int mid = (l+r) >> 1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    tree[p].maxn = max(tree[lc].maxn,tree[rc].maxn);
    tree[p].minn = min(tree[lc].minn,tree[rc].minn);
}
pii query(int p,int l,int r){
    //完全在范围内
    if(tree[p].l==l&&tree[p].r==r){
        return make_pair(tree[p].maxn,tree[p].minn);
    }
    int mid = (tree[p].l+tree[p].r) >> 1;
    //如果在左儿子的范围
    if(r<=mid){
        return query(lc,l,r);
        //如果在右儿子的范围
    }else if(l > mid){
        return query(rc,l,r);
      //如果是跨越的,分别求
    }else{
        pii p1 = query(lc,l,mid);
        pii p2 = query(rc,mid+1,r);
        return make_pair(max(p1.first,p2.first),min(p1.second,p2.second));
    }
}
int main()
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int n,q;
    cin >> n >> q;
    for(int i=1;i<=n;i++) cin >> a[i];
    build(1,1,n);
    for(int i=1;i<=q;i++){
        int l,r;
        cin >> l >> r;
        pii p = query(1,l,r);
        cout << (p.first - p.second) << '\n';
    }
    return 0;
}

稍微解释一下几个query之间的值是怎么填的

1.如果完全坐落于当前节点所在的区间内;直接返回当前节点的就行;

//完全在范围内
    if(tree[p].l==l&&tree[p].r==r){
        return make_pair(tree[p].maxn,tree[p].minn);
    }

如果完全位于左子树,那么这是一个子问题。我们递归解决即可;

//如果在左儿子的范围
    if(r<=mid){
        return query(lc,l,r);
    }

如果完全位于右子树,那么同理有

//如果在右儿子的范围
    }else if(l > mid){
        return query(rc,l,r);
    }

如果是横跨左右子树,那么我们得算好范围;

因为是横跨左右子树的
那么r必然大于mid
而l必然小于等于mid
(不理解的话看上面if条件就知道啦~)
显然区间[l,r]=[l,mid]+[mid+1,r]
那么对于左子树来说,需要查询的范围是[l,mid]
对于右子树来说,需要查询的范围是[mid+1,r]
分别查询后,再拼出结果即可。

那么就可以得到以下代码

else{
        pii p1 = query(lc,l,mid);
        pii p2 = query(rc,mid+1,r);
        return make_pair(max(p1.first,p2.first),min(p1.second,p2.second));
    }

问题二、

HDU4027
在这里插入图片描述
在这里插入图片描述
因为是算术平方根,我们没法直接区间操作;

因此这题是一个单点操作,区间查询的题目;

很容易想到,1的算术平方根还是它本身;

因此,如果某一个区间里面的数据都是1,我们就不要浪费时间去更新;

判断的方法很简单,看长度即可;

Code

#include <iostream>
#include <cmath>
#define lc (p<<1)
#define rc (p<<1|1)
using namespace std;
const int N = 100000+10;
typedef long long ll;
struct Node{
    int l,r;
    ll sum;
}tree[N<<2];
int INF = (1<<29);
ll a[N];
void push_up(int p){
    tree[p].sum = tree[lc].sum + tree[rc].sum;
}
void build(int p,int l,int r){
    tree[p].l=l,tree[p].r=r;
    if(l==r){
        tree[p].sum = a[l];
        return;
    }
    int mid = (l+r) >> 1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    push_up(p);
}
void push_down(int p){
    //如果是叶子
    if(tree[p].l==tree[p].r){
        tree[p].sum = (ll)sqrt(tree[p].sum);
        return;
    }
    push_down(lc);
    push_down(rc);
    push_up(p);
}
void update(int p,int l,int r){
    if(tree[p].l==l&&tree[p].r==r){
        if(r-l+1==tree[p].sum) return;
        push_down(p);
        return;
    }
    int mid = (tree[p].l+tree[p].r)>>1;
    if(r<=mid){
        update(lc,l,r);
    }else if(l>mid){
        update(rc,l,r);
    }else{
        update(lc,l,mid);
        update(rc,mid+1,r);
    }
    push_up(p);
}
ll query(int p,int l,int r){
    if(tree[p].l==l&&tree[p].r==r){
        return tree[p].sum;
    }
    int mid = (tree[p].l+tree[p].r)>>1;
    if(r<=mid){
        return query(lc,l,r);
    }else if(l>mid){
        return query(rc,l,r);
    }else{
        return query(lc,l,mid) + query(rc,mid+1,r);
    }
}
int main()
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int n,m,t,x,y;
    int num = 0;
    while(cin>>n){
        cout << "Case #"<< (++num) << ":\n";
        for(int i=1;i<=n;i++) cin >> a[i];
        build(1,1,n);
        cin >> m;
        for(int i=1;i<=m;i++){
            cin >> t >> x >> y;
            if(x>y) swap(x,y);
            if(t==1){
                cout << query(1,x,y) << '\n';
            }else{
                update(1,x,y);
            }
        }
        cout << '\n';
    }
    return 0;
}

问题三、

POJ2481
在这里插入图片描述
在这里插入图片描述
其实就是在问:对于每一个闭区间,求它是多少个区间的真子集。

首先我们按区间左端点 S为第一关键字,升序排序;如果 S 相等则对右端点 E 进行降序排序;

排序后我们进行遍历,现在已经保证了越靠前的范围越大;

如果i<j 那么必然有Si <= Sj

当Si 等于 Sj 的时候,如果 Ti 也等于 Tj,那么直接继承;

否则

我们只需要去查找有多少个右端点

比当前枚举到的这个点的右端点大;

就可以得到真子集了;

#include <iostream>
#include <algorithm>
#include <cstdio>
#define lc (p<<1)
#define rc (p<<1|1)
using namespace std;
const int N = 1e5+10;
struct Cow{
    Cow(int l,int r,int p) :l(l),r(r),pos(p){}
    Cow(){}
    int l,r,pos;
}c[N];
struct Tree{
    int l,r,sum;//sum存的是目前区间内有的右端点个数
    //对于指定的右端点,只需要query有多少个右端点是≥它的
}tree[N<<2];
bool cmp(Cow a,Cow b){
    if(a.l==b.l) return a.r > b.r;
    return a.l < b.l;
}
void build(int p,int l,int r){
    tree[p].l=l,tree[p].r=r,tree[p].sum=0;
    if(l==r){
        return;
    }
    int mid = (l+r)>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
}
int ans[N];
int query(int p,int l,int r){
    if(tree[p].l>=l&&tree[p].r<=r){
        return tree[p].sum;
    }
    int mid = (tree[p].l+tree[p].r) >> 1;
    if(mid>=r){
        return query(lc,l,r);
    }else if(l>mid){
        return query(rc,l,r);
    }else{
        return query(lc,l,mid)+query(rc,mid+1,r);
    }
}
void update(int p,int target){
    if(tree[p].l==tree[p].r){
        tree[p].sum++;
        return;
    }
    int mid = (tree[p].l+tree[p].r) >> 1;
    if(target<=mid){
        update(lc,target);
    }else{
        update(rc,target);
    }
    tree[p].sum = tree[lc].sum + tree[rc].sum;
}
int main()
{
    //std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int n,l,r;
    while(~scanf("%d",&n)&&n){
        int maxn = 0;
        for(int i=1;i<=n;i++){
            //cin >> l >> r;
            scanf("%d%d",&l,&r);
            maxn = max(maxn,r);
            c[i] = Cow(l,r,i);
        }
        sort(c+1,c+1+n,cmp);
        build(1,1,maxn);
        for(int i=1;i<=n;i++){
            if(i!=1&&c[i].l==c[i-1].l&&c[i].r==c[i-1].r){
                ans[c[i].pos] = ans[c[i-1].pos];
            }else{
                ans[c[i].pos] = query(1,c[i].r,maxn);
            }
            //当前右端点所属的区间都需要更新
            update(1,c[i].r);
        }
        for(int i=1;i<=n;i++){
            printf("%d ",ans[i]);
            //cout << ans[i] << ' ';
        }
        putchar('\n');
        //cout << '\n';
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值