5017: [Snoi2017]炸弹

题目链接

题目大意:在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足:
Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被引爆。
现在,请你帮忙计算一下,先把第 i 个炸弹引爆,将引爆多少个炸弹呢?

题解:若x能引爆y,add(x,y),易知i的答案即为i能到达的编号max-编号min+1

考虑到i的连边是一个区间,可以用线段树优化建图

跑一遍Tarjan缩强连通分量,然后原图变成一个DAG,接下来跑个拓扑序,然后倒过来扫一遍即可

我的收获:各种套路……

#include <bits/stdc++.h>
using namespace std;

const int P=1e9+7;

#define INF LLONG_MAX/2
#define ll long long 
#define N 2500005

int n,sz,num[N];
ll X[N],R[N];
int t,head[N];
int dfn[N],low[N],col[N],in[N];
int scnt,tim,stk[N],top;
ll mi[N],mx[N];
bool ins[N];

vector<int> G[N],q;

struct edge{int fr,to,nex;}e[N<<3];

void add(int u,int v){e[t].fr=u,e[t].to=v,e[t].nex=head[u],head[u]=t++;}

struct SegmentTree{

    #define ls x<<1
    #define rs x<<1|1
    #define lson l,mid,x<<1
    #define rson mid+1,r,x<<1|1
    #define root 1,n,1

    void build(int l,int r,int x)
    {
        num[x]=++sz;
        if(l==r){add(num[x],l);return ;}
        int mid=l+r>>1;
        build(lson),build(rson);
        add(num[x],num[ls]),add(num[x],num[rs]);
    }

    void update(int L,int R,int ot,int l,int r,int x)
    {
        if(L<=l&&r<=R){add(ot,num[x]);return ;}
        int mid=l+r>>1;
        if(L<=mid) update(L,R,ot,lson);
        if(R>mid) update(L,R,ot,rson);
    }

}T;

void tarjan(int x){
    int now=0;
    dfn[x]=low[x]=++tim;
    stk[++top]=x;ins[x]=1;
    for(int i=head[x];i!=-1;i=e[i].nex){
        int v=e[i].to;
        if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
        else if(ins[v]) low[x]=min(low[x],dfn[v]);
    }
    if(low[x]==dfn[x]){
        scnt++;mi[scnt]=INF,mx[scnt]=-INF;
        while(x!=now){
            now=stk[top--];ins[now]=0;col[now]=scnt;
            if(now<=n) mi[scnt]=min(mi[scnt],(ll)now),mx[scnt]=max(mx[scnt],(ll)now);
        }
    }
}

void rebuild()
{
    for(int i=0;i<t;i++)
        if(col[e[i].fr]!=col[e[i].to])
            G[col[e[i].fr]].push_back(col[e[i].to]),in[col[e[i].to]]++;
}

void toposort()
{
    for(int i=1;i<=scnt;i++) if(!in[i]) q.push_back(i);
    for(int x=0;x<q.size();x++)
    {
        int u=q[x];
        for(int i=0;i<G[u].size();i++){
            int v=G[u][i];
            in[v]--;if(!in[v]) q.push_back(v);
        }
    }
    for(int i=scnt;i;i--){
        int x=q[i-1];
        for(int j=0;j<G[x].size();j++) mi[x]=min(mi[x],mi[G[x][j]]),mx[x]=max(mx[x],mx[G[x][j]]);
    }
}

void work()
{
    for(int i=1;i<=sz;i++) if(!dfn[i]) tarjan(i);
    rebuild();toposort();
    ll ans=0;
    for(int i=1;i<=n;i++) ans+=1ll*i*(mx[col[i]]-mi[col[i]]+1),ans%=P; 
    printf("%lld\n",ans);
}

void init()
{
    memset(head,-1,sizeof(head));
    scanf("%d",&n);sz=n;T.build(root);                                                  
    for(int i=1;i<=n;i++) scanf("%lld%lld",&X[i],&R[i]);
    for(int i=1;i<=n;i++) T.update(lower_bound(X+1,X+n+1,X[i]-R[i])-X,upper_bound(X+1,X+n+1,X[i]+R[i])-X-1,i,root);
}

int main()
{
    init();
    work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值