P1204 [USACO1.2] 挤牛奶Milking Cows 线段树

在洛谷上搜线段树的题,就找到了这个,就用线段树的模板写一下。

题目链接:挤牛奶Milking Cows - 洛谷

由于题目中lr的范围只有1e6,因此可以用线段树进行维护

线段树中每个线段表示该区间内有多少人在工作,比如题目的样例

3
300 1000
700 1200
1500 2100

其中线段[300,1000]上每个位置都是1,那么query(300,1000)得到的结果是700+1,因为两端都包含了,所以只需要对l,r进行更新即可

使用二分进行答案的查找,对每个答案进行判断是否可行

判断第一个的时候要与x+1进行比较,因为两端都包含,判断第二个要让最后的结果+2,因为两端都不包含:

比如:[1,4]、[6,8],其中最长的是【1,4】共5个贡献,没有工作的则是1

判断的范围要在题目所给的lr之间

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long

const int N = 2e6+10;
int tree[N * 4];
int a[N],tag[N*4];
inline int ls(int p){return p<<1ll;}
inline int rs(int p){return p<<1ll|1ll;}

// 从下向上传递区间值
void push_up(int p){
    tree[p] = tree[ls(p)] + tree[rs(p)];//区间和
    // tree[p] = max(tree[ls(p)],tree[rs(p)]);//最值
}
// 建树
void build(int p,int pl,int pr){
    tag[p]=0;
    if(pl==pr){
        tree[p] = a[pl];
        return;
    }
    int mid = (pl + pr) / 2;
    build(ls(p),pl,mid);//递归左儿子
    build(rs(p),mid+1,pr);//递归右儿子
    push_up(p);// 从下往上传递区间值
}
void addtag(int p,int pl,int pr,int d){
    tag[p] = d;
    tree[p] = d*(pr-pl+1);
}
void push_down(int p,int pl,int pr){
    if(tag[p] != 0){
        int mid = (pl + pr) / 2;
        addtag(ls(p),pl,mid,tag[p]);
        addtag(rs(p),mid+1,pr,tag[p]);
        tag[p] = 0;
    }

}
void update(int L,int R,int p,int pl,int pr,int d){
    if (L <= pl && pr <= R){
        addtag(p,pl,pr,d);
        return;
    }
    push_down(p,pl,pr);
    int mid = (pl + pr) / 2;
    if(L <= mid) update(L,R,ls(p),pl,mid,d);
    if(R > mid) update(L,R,rs(p),mid+1,pr,d);
    push_up(p);
}
// 查询 [L,R]区间的区间和,递归到节点p,该节点的范围是[pl,pr]
int query(int L,int R,int p,int pl,int pr){

    if(L <= pl && R >= pr){ //完全覆盖
        return tree[p];
    }
    push_down(p,pl,pr);
    int mid = (pl + pr) / 2;
    int res = 0;
    if(L <= mid) res += query(L,R,ls(p),pl,mid); //L与左儿子有重叠
    if(R > mid) res += query(L,R,rs(p),mid+1,pr); //R与有儿子有重叠
    return res;
    // 调用方式:query(L,R,1,1,n,d);//即从根节点1开始查询,改节点的范围是[1,n],查询范围[L,R]
}
int maxn = 2e6;
int lc = maxn,rc = 0;
int n;
bool check1(int x){
    for(int i = lc ; i + x <= rc ; i ++){
        if(query(i,i+x,1,1,maxn) == x+1)return true;
    }
    return false;
}
bool check2(int x){
    for(int i = lc ; i + x <= rc ; i ++){
        if(query(i,i+x,1,1,maxn) == 0) {
            return true;
        }
    }
    return false;
}
void solve(){
    
    cin >> n;
    int x,y;
    for(int i = 1 ; i <= n ; i ++){
        cin >> x >> y;
        x++,y++;
        lc = min(lc,x);
        rc = max(rc,y);
        update(x,y,1,1,maxn,1);
    }
    int l = 0,r = 1e6+10;
    int ans1 = 0;
    int ans2 = 0;
    while(l <= r){
        int mid = l + r >> 1;
        if(check1(mid)){
            ans1 = max(ans1,mid);
            l = mid + 1;
        }else{
            r = mid - 1;
        }
    }
    l = 0,r = 2e6+10;
    while(l <= r){
        int mid = l + r >> 1;
        if(check2(mid)){
            ans2 = max(ans2,mid);
            l = mid + 1;
        }else{
            r = mid - 1;
        }
    }
    cout<<ans1<<' '<<(ans2 == 0 ? 0 : ans2+2)<<'\n';
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t = 1;
    while(t--){
        solve();
    }
    
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贰叁肆775

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值