cursor: url(https://files-cdn.cnblogs.com/files/zouwangblog/cursor.ico),auto;

[SNOI2017]炸弹

 

题解:

首先这题Xi严格递增,所以就少了个排序。

网上几乎所有标都是线段树优化建图然后再tarjan。本人在考试时想了个纯线段树做法也A了。

线段树的点不是代表坐标区间,而是点编号的区间,因为炸弹爆炸一定是一段连续的编号。

线段树每个点除了记编号区间外还记了这个区间的炸弹爆炸能影响的范围[L,R]。

根据这个[L,R]我们可以lower_bound出这个区间最左边的炸弹编号和最右边的炸弹编号,再查询线段树查看爆炸范围是否扩大,是则循环,否则返回记答案。

这个方法是很好写的,码量也较少。

(考试时我一直在想一个事情,如果炸弹太密集了怎么办...后来发现不会有这种情况,因为这样即使是建边会建炸的,害得我琢磨了好久)

 

#include<bits/stdc++.h>
#define ll long long
#define lc x<<1
#define rc x<<1|1
using namespace std;
const int N=5e5+5,mod=1e9+7;
ll Get(){
    ll x=0,f=1;char c=0;
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c))x=x*10+c-'0',c=getchar();
    return x*f;
}
int n;ll ans,a[N],L,R;
struct node{ll x,r;}p[N];
struct Seg{int l,r;ll mnl,mxr;}T[N*4];
bool cmp(node a,node b){return a.x<b.x;}
void bui(int x,int l,int r){
    T[x].l=l,T[x].r=r;
    if(l==r){T[x].mnl=p[l].x-p[l].r,T[x].mxr=p[l].x+p[l].r;return;}
    int mid=l+r>>1;
    bui(lc,l,mid),bui(rc,mid+1,r);
    T[x].mnl=min(T[lc].mnl,T[rc].mnl);
    T[x].mxr=max(T[lc].mxr,T[rc].mxr);//更新爆炸范围
}
void ask(int x,int l,int r){
    if(T[x].l>=l&&T[x].r<=r){L=min(L,T[x].mnl),R=max(R,T[x].mxr);return;}
    int mid=T[x].l+T[x].r>>1;
    if(mid>=r)ask(lc,l,r);
    else if(mid<l)ask(rc,l,r);
    else ask(lc,l,r),ask(rc,l,r);
}
int main()
{
    int i;ll l,r,tmpl,tmpr;
    n=Get();
    for(i=1;i<=n;i++)a[i]=p[i].x=Get(),p[i].r=Get();
    bui(1,1,n);
    for(i=1;i<=n;i++){
        l=lower_bound(a+1,a+1+n,p[i].x-p[i].r)-a;
        r=lower_bound(a+1,a+1+n,p[i].x+p[i].r)-a;
        if(a[r]>p[i].x+p[i].r||r>n)r--;
        tmpl=tmpr=0;
        while((tmpl^l)||(tmpr^r)){
            tmpl=l,tmpr=r;
            L=1e18,R=-1e18;
            ask(1,l,r);
            l=lower_bound(a+1,a+1+n,L)-a;
            r=lower_bound(a+1,a+1+n,R)-a;
            if(a[r]>R||r>n)r--;
        }//这就是上述的那个循环
        ans=(ans+i*(r-l+1))%mod;
    }
    cout<<ans;
}
View Code
posted @ 2018-10-01 10:32  Art-Yrr  阅读(288)  评论(0)    收藏  举报