hdu 6521 Party (吉司机线段树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6521

题意:给定n,m表示有编号1~n的人,m次询问,每次询问给出l,r问这个区间内的人有多少两两组合在之前没有出现过,对于每次询问输出符合的组数。

例如[1,4] 可以有 1-2 1-3 1-4 2-3 2-4 3-4 6种

再给你[2,5] 则只有 2-5 3-5 4-5 3种(其余的上面已经出现过)

数据范围:n≤5e5,m≤5e5

 

思路:吉司机线段树,在线段树上维护一个最大值,这个最大值代表对于线段树这个节点管辖的区间内每个点之前被输出的最靠左的位置比如上面的[1,4],那么这个区间内的每一个点的ma就是1,向上更新时要把区间内最大的那个左位置传上去。访问时如果对于线段树上的这个节点上它所有管辖的点的最左的最大值都小于我要查询的区间的l,那么这个线段树的节点的贡献就是0,如果这个节点上管辖的点的最左有落在查询的这个区间[l,r]内那么只能一直更新到子节点,计算贡献就是tree[x].ma-l,然后全部求和就是答案。

#include <bits/stdc++.h>
#define ls (x<<1)
#define rs (x<<1|1)
using namespace std;
typedef long long ll;
const int N=5e5+5;
int n,m,t;
struct Tree{
    int l,r,ma;
}tree[N<<2];
void build(int x,int l,int r){
    tree[x].l=l,tree[x].r=r,tree[x].ma=0;
    if(l==r)tree[x].ma=l;
    else{
        int mid=(l+r)>>1;
        build(ls,l,mid);
        build(rs,mid+1,r);
        tree[x].ma=max(tree[ls].ma,tree[rs].ma);
    }
}
ll update(int x,int l,int r,int pos){
    ll res=0;int L=tree[x].l,R=tree[x].r;
    int mid=(L+R)>>1;
    if(tree[x].ma<=pos)return res;
    if(L==R){
        res=tree[x].ma-pos;
        tree[x].ma=pos;
        return res;
    }
    if(l<=mid)res+=update(ls,l,r,pos);
    if(r>mid)res+=update(rs,l,r,pos);
    tree[x].ma=max(tree[ls].ma,tree[rs].ma);
    return res;
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        build(1,1,n);
        int l,r;
        for(int i=1;i<=m;i++){
            scanf("%d%d",&l,&r);
            printf("%lld\n",update(1,l,r,l));
        }
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值