[bzoj5017/Snoi2017]炸彈

題目大意

在一條直線上有 N 個炸彈,每個炸彈的座標是 Xi,爆炸半徑是 Ri,當一個炸彈爆炸時,如果另一個炸彈所在位置 Xj 滿足:
Xi−Ri≤Xj≤Xi+Ri,那麼,該炸彈也會被引爆。
對於i等於1到n,求把第i個炸彈引爆會導致多少個炸彈爆炸。

n≤500000,|Xi|≤1018 ,0≤Ri≤21018 ,保證Xi嚴格遞增

分析

首先引爆的炸彈肯定是一個區間。
那麼可以找引爆i後最終爆炸區間的兩個端點來求答案。
可以嘗試給i向j連有向邊邊,表示i爆炸後可以直接引爆j,然後在i能到達的點裏面找最小、最大值。
但是這樣連邊的複雜度很大,注意到直接引爆的也是一個區間,那麼用線段樹優化連邊即可。
接下來就是找一個點能到達的點的編號的極值。可以跑一遍Tarjan縮強連通分量,然後原圖變成一個DAG,接下來跑個拓撲序,然後倒過來掃一遍即可。
時間複雜度O(nlogn)

#include <cstdio>
#include <cstring>
#include <algorithm>

#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))

using namespace std;

const int N=5e5+5,Log=19,mo=1e9+7,M=1048588,T=N*45;

typedef long long LL;

int n,m,ans,tot,h[M],e[T],nxt[T],Id[M],H[M],E[T],Nxt[T],dfn[M],low[M],Top,st[M],Mi[M],Mx[M],ID[N],D[M],de[M];

bool vis[M],bz[M];

LL X[N],R[N];

char c;

LL read()
{
    LL x=0; int sig=1;
    for (c=getchar();c<'0' || c>'9';c=getchar()) if (c=='-') sig=-1;
    for (;c>='0' && c<='9';c=getchar()) x=x*10+c-48;
    return x*sig;
}

void add(int x,int y)
{
    e[++tot]=y; nxt[tot]=h[x]; h[x]=tot;
}

void Out(int l,int r,LL a,LL b,int v,int x)
{
    if (a<=X[l] && b>=X[r])
    {
        add(ID[v],x); return;
    }
    int mid=l+r>>1;
    if (a<=X[mid]) Out(l,mid,a,b,v,x<<1);
    if (b>=X[mid+1]) Out(mid+1,r,a,b,v,x<<1|1);
}

void Link(int l,int r,int x)
{
    if (l==r)
    {
        ID[l]=x; return;
    }
    int mid=l+r>>1;
    add(x,x<<1); add(x,x<<1|1);
    Link(l,mid,x<<1); Link(mid+1,r,x<<1|1);
}

void Tarjan(int x)
{
    low[x]=dfn[x]=++tot;
    vis[x]=bz[x]=1; st[++Top]=x;
    for (int i=h[x];i;i=nxt[i])
    {
        if (!vis[e[i]])
        {
            Tarjan(e[i]); low[x]=min(low[x],low[e[i]]);
        }else if (bz[e[i]]) low[x]=min(low[x],low[e[i]]);
    }
    if (dfn[x]==low[x])
    {
        m++;
        for (;st[Top+1]!=x;Id[st[Top--]]=m) bz[st[Top]]=0;
    }
}

void Add(int x,int y)
{
    E[++tot]=y; Nxt[tot]=H[x]; H[x]=tot; de[y]++;
}

int main()
{
    n=read();
    for (int i=1;i<=n;i++) X[i]=read(),R[i]=read();
    Link(1,n,1);
    for (int i=1;i<=n;i++) Out(1,n,X[i]-R[i],X[i]+R[i],i,1);
    tot=0;
    for (int i=1;i<=ID[n];i++) if (!vis[ID[i]]) Tarjan(ID[i]);
    tot=0;
    for (int i=1;i<M;i++)
        for (int j=h[i];j;j=nxt[j]) if (Id[i]!=Id[e[j]]) Add(Id[i],Id[e[j]]);
    memset(Mi,127,sizeof(Mi));
    for (int i=1;i<=n;i++) Mi[Id[ID[i]]]=min(Mi[Id[ID[i]]],i),Mx[Id[ID[i]]]=max(Mx[Id[ID[i]]],i);
    tot=0;
    for (int i=1;i<=m;i++) if (!de[i]) D[++tot]=i;
    for (int i=1,j,x;i<=tot;i++)
    {
        x=D[i];
        for (j=H[x];j;j=Nxt[j])
        {
            de[E[j]]--;
            if (!de[E[j]]) D[++tot]=E[j];
        }
    }
    for (int i=n,j,x;i;i--)
    {
        x=D[i];
        for (j=H[x];j;j=Nxt[j]) Mi[x]=min(Mi[x],Mi[E[j]]),Mx[x]=max(Mx[x],Mx[E[j]]);
    }
    ans=0;
    for (int i=1;i<=n;i++) ans=(ans+1ll*i*(Mx[Id[ID[i]]]-Mi[Id[ID[i]]]+1))%mo;
    printf("%d\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章