Codeforces Round #810 (Div. 2) D. Rain (线段树差分)

Codeforces Round #810 (Div. 2) D. Rain (线段树差分)

前言: 一道恶心了我近一天的题,共 180 180 180 行重复敲了 3 3 3 次 , 不知道 d d d 了多久的 b u g bug bug ,还是线段树专题做的太少了。


题意: 给定一个 n n n ,这 n n n 天会降雨,每一个降雨天有一个 x x x 和一个 p p p ,表示自 x x x 为中心,降雨量向左右两边递减,例如 x = 3 x=3 x=3 p = 5 p=5 p=5 的情况为:

0 1 2 3 4 5 4 3 2 1 0
-2 -1 0 1 2 3 4 5 6 7 8

问:统计将任意一天天气变成非降雨天,所有地的最大降雨量是否不超过 m m m ,不超过输出 1 1 1 否则输出 − 1 -1 1


思路: 首先本题很明显得知具有差分性,即降雨中心向左右两边以公差 d = 1 d=1 d=1 逐步递减,通过 无聊的数列(经典线段树差分) 可知我们可以在 O ( n l o g n ) O(nlogn) O(nlogn) 的时间复杂度内计算出这 n n n 天都下雨情况下所有降雨中心的 降雨值 这可以通过一个线段树实现。剩下的步骤就是枚举 1 1 1 ~ n n n 中的某一天不降雨情况下,所有地的降雨最大值是否小于 m m m ,我们开第二棵线段树用来维护三个最大值即 h [ l ] 、 h [ l ] − v e c [ l ] 、 h [ l ] + v e c [ l ] h[l]、h[l]-vec[l]、h[l]+vec[l] h[l]h[l]vec[l]h[l]+vec[l] 其中第一个 h [ l ] h[l] h[l] 表示不被当前枚举的降雨天所涉及情况下各地中降雨的最大值,因为降水量不会减少 。 其余两个分别是上坡与下坡的 偏移量写法 ,用来将同一斜线上的数置于同一水平上比较,得出区间降水最大值。


代码:

/*
 * @author:  Snow
 * @Description:  Algorithm Contest
 * @LastEditTime: 2022-07-27 10:51:20
 */
#include<bits/stdc++.h>
using namespace std;
#define int long long 
#pragma GCC optimize(3)
#define re register int
typedef pair<int,int>PII;
#define pb emplace_back
#define debug(a) cout<<a<<' ';
#define fer(i,a,b) for(re i=a;i<=b;i++)
#define der(i,a,b) for(re i=a;i>=b;i--)
int n,m;
const int N = 2e5+10;
int x[N],p[N];
vector<int>vec2;
int vec[N*4];
int h[N*4];
char ans[N];
struct Seg_tree1{
    struct Node{
        int l,r,k,d;
    }tr[N*16];
    void build(int u,int l,int r){
        if(l==r){
            tr[u]={l,r,0,0};
        }
        else{
            int mid=l+r>>1;
            tr[u]={l,r};
            build(u<<1,l,mid);
            build(u<<1|1,mid+1,r);
        }
    }
    void pushdown(int u){
        if(tr[u].k||tr[u].d){
            int mid=tr[u].l+tr[u].r>>1;
            tr[u<<1].k+=tr[u].k;
            tr[u<<1|1].k+=tr[u].k+(vec[tr[u<<1|1].l]-vec[tr[u].l])*tr[u].d;
            tr[u<<1].d+=tr[u].d;
            tr[u<<1|1].d+=tr[u].d;
            tr[u].d=tr[u].k=0;
        }
    }
    void modify(int u,int l,int r,int k,int d){
        if(tr[u].l>=l&&tr[u].r<=r){
            tr[u].k+=k+(vec[tr[u].l]-vec[l])*d;
            tr[u].d+=d;
        }
        else{
            pushdown(u);
            int mid=tr[u].l+tr[u].r>>1;
            if(l<=mid)modify(u<<1,l,r,k,d);
            if(r>mid)modify(u<<1|1,l,r,k,d);
        }
    }
    void floor(int u){
        if(tr[u].l==tr[u].r){
            h[tr[u].l]=tr[u].k;
        }
        else{
            pushdown(u);
            floor(u<<1);
            floor(u<<1|1);
        }
    }
}seg1;
struct Seg_tree2{
    struct Node{
        int l,r,v1,v2,v3;
    }tr[N*16];
    void pushup(int u){
        tr[u].v1=max(tr[u<<1].v1,tr[u<<1|1].v1);
        tr[u].v2=max(tr[u<<1].v2,tr[u<<1|1].v2);
        tr[u].v3=max(tr[u<<1].v3,tr[u<<1|1].v3);
    }
    void build(int u,int l,int r){
        if(l==r){
            tr[u]={l,r,h[l],h[l]-vec[l],h[l]+vec[l]};
        }
        else{
            tr[u]={l,r};
            int mid=l+r>>1;
            build(u<<1,l,mid);
            build(u<<1|1,mid+1,r);
            pushup(u);
        }
    }
    Node query(int u,int l,int r){
        if(tr[u].l>=l&&tr[u].r<=r){
            return tr[u];
        }
        int mid=tr[u].l+tr[u].r>>1;
        if(r<=mid)return query(u<<1,l,r);
        if(l>mid)return query(u<<1|1,l,r);
        Node res;
        Node left=query(u<<1,l,r);
        Node right=query(u<<1|1,l,r);
        res.v1=max(left.v1,right.v1);
        res.v2=max(left.v2,right.v2);
        res.v3=max(left.v3,right.v3);
        return res;
    }
}seg2;
void cf(){
    vec2.clear();
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>x[i]>>p[i];
    for(int i=1;i<=n;i++){
        vec2.push_back(x[i]);
        vec2.push_back(x[i]-p[i]+1);
        vec2.push_back(x[i]+1);
        vec2.push_back(x[i]+p[i]-1);
    }
    sort(vec2.begin(),vec2.end());
    vec2.erase(unique(vec2.begin(),vec2.end()),vec2.end());
    for(int i=1;i<=vec2.size();i++)vec[i]=vec2[i-1];
    int len=vec2.size();
    seg1.build(1,1,len);
    for(int i=1;i<=n;i++){
        int l=lower_bound(vec+1,vec+1+len,x[i]-p[i]+1)-vec;
        int r=lower_bound(vec+1,vec+1+len,x[i])-vec;
        if(l<=r){
            seg1.modify(1,l,r,1,1);
        } 
        l=lower_bound(vec+1,vec+1+len,x[i]+1)-vec;
        r=lower_bound(vec+1,vec+1+len,x[i]+p[i]-1)-vec;
        if(l<=r){
            seg1.modify(1,l,r,p[i]-1,-1);
        }
    }
    seg1.floor(1);
    seg2.build(1,1,len);
    fer(i,1,n){
        int maxv=0;
        int l=lower_bound(vec+1,vec+1+len,x[i]-p[i]+1)-vec;
        int r=lower_bound(vec+1,vec+1+len,x[i])-vec;
        if(l>1){
            int t=seg2.query(1,1,l-1).v1;
            maxv=max(maxv,t);
        }
        if(l<=r){
            int t=seg2.query(1,l,r).v2;
            maxv=max(maxv,t+vec[l]-1);
        }
        l=lower_bound(vec+1,vec+1+len,x[i]+1)-vec;
        r=lower_bound(vec+1,vec+1+len,x[i]+p[i]-1)-vec;
        if(r+1<=len){
            int t=seg2.query(1,r+1,len).v1;
            maxv=max(maxv,t);
        }
        if(l<=r){
            int t=seg2.query(1,l,r).v3;
            maxv=max(maxv,t-vec[r]-1);
        }
        ans[i]=maxv<=m?'1':'0';
    }
    fer(i,1,n)cout<<ans[i];
    cout<<endl;
}
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int _=1;
    cin>>_;
    while(_--){
        cf();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值