BZOJ 5017 [Snoi2017]炸弹

题解:每个炸弹爆炸影响一个区间,通过二分查找找到

若A爆炸炸到B则连一条A到B的边

线段树优化建图

缩点+DP

因为每个炸弹的答案一定是一个区间,所以记录每个节点的左端点和右端点

合并时取最值

反思:思维定式,以为求解可达点个数不能合并

/**************************************************************
    Problem: 5017
    User: ws_zzyer
    Language: C++
    Result: Accepted
    Time:5928 ms
    Memory:390956 kb
****************************************************************/
 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define lo now<<1
#define ro now<<1|1 
using namespace std;
const int maxn=1600000;
const int oo=1000000000;
const int mm=1000000007;
 
int n,totn;
long long a[maxn];
long long r[maxn];
int tl[maxn],tr[maxn];
 
int cntedge;
int head[maxn];
int to[30000009],nex[30000009];
void Addedge(int x,int y){
    nex[++cntedge]=head[x];
    to[cntedge]=y;
    head[x]=cntedge;
}
 
struct SegmentTree{
    int l,r;
    int u;
}tree[500009<<2];
void BuildTree(int now,int l,int r){
    tree[now].l=l;tree[now].r=r;
    tree[now].u=++totn;
    for(int i=l;i<=r;++i)Addedge(totn,i);
    if(l==r)return;
    int mid=(l+r)>>1;
    BuildTree(lo,l,mid);
    BuildTree(ro,mid+1,r);
}
void Updatasec(int now,int ll,int rr,int x){
    if(tree[now].l>=ll&&tree[now].r<=rr){
        Addedge(x,tree[now].u);
        return;
    }
    int mid=(tree[now].l+tree[now].r)>>1;
    if(ll<=mid)Updatasec(lo,ll,rr,x);
    if(rr>mid)Updatasec(ro,ll,rr,x);
}
 
int dfsclock,scccnt;
int pre[maxn],lowlink[maxn],sccno[maxn];
int lb[maxn],rb[maxn];
int S[maxn],top;
void Dfs(int u){
    pre[u]=lowlink[u]=++dfsclock;
    S[++top]=u;
     
    for(int i=head[u];i;i=nex[i]){
        int v=to[i];
        if(!pre[v]){
            Dfs(v);
            lowlink[u]=min(lowlink[u],lowlink[v]);
        }else if(!sccno[v]){
            lowlink[u]=min(lowlink[u],pre[v]);
        }
    }
     
    if(lowlink[u]==pre[u]){
        ++scccnt;
        for(;;){
            int x=S[top--];
            sccno[x]=scccnt;
            if(x==u)break;
        }
    }
}
 
 
vector<int>G[maxn];
void Tarjan(){
    for(int i=1;i<=totn;++i)if(!pre[i])Dfs(i);
    for(int i=1;i<=totn;++i){
        lb[i]=oo;rb[i]=-oo;
    }
    for(int i=1;i<=n;++i){
        int x=sccno[i];
        lb[x]=min(lb[x],tl[i]);
        rb[x]=max(rb[x],tr[i]);
    }
     
    for(int u=1;u<=totn;++u){
        for(int i=head[u];i;i=nex[i]){
            int v=to[i];
            if(sccno[u]!=sccno[v]){
                G[sccno[u]].push_back(sccno[v]);
            }
        }
    }
}
 
int vis[maxn];
void Dp(int x){
    if(vis[x])return;
    vis[x]=1;
    for(int i=0;i<G[x].size();++i){
        int v=G[x][i];
        Dp(v);
        lb[x]=min(lb[x],lb[v]);
        rb[x]=max(rb[x],rb[v]);
    }
}
 
int main(){
    scanf("%d",&n);totn=n;
    for(int i=1;i<=n;++i){
        scanf("%lld%lld",&a[i],&r[i]);
    }
    BuildTree(1,1,n);
    for(int i=1;i<=n;++i){
        tl[i]=lower_bound(a+1,a+1+n,a[i]-r[i])-a;
        tr[i]=upper_bound(a+1,a+1+n,a[i]+r[i])-a-1;
//      cout<<tl[i]<<' '<<tr[i]<<endl;
        Updatasec(1,tl[i],tr[i],i);
    } 
    Tarjan();
    for(int i=1;i<=totn;++i)if(!vis[i])Dp(i);
     
    long long ans=0;
    for(int i=1;i<=n;++i){
//      cout<<rb[sccno[i]]-lb[sccno[i]]<<endl;
        ans=(ans+1LL*(1+rb[sccno[i]]-lb[sccno[i]])*i)%mm;
    }
     
    printf("%lld\n",ans);
    return 0;
}

  

 

转载于:https://www.cnblogs.com/zzyer/p/8476008.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值