P5025 [SNOI2017]炸彈

第一篇黑題題解qwq

因爲窩在作zhx的模擬題被2-SAT加線段樹優化建邊搞炸了,因此一氣之下來學了這兩個東西wwnode

這篇就是線段樹優化建邊c++

直接步入正題,先來看題目優化

題目描述

在一條直線上有\(N\)個炸彈,每一個炸彈的座標是 \(X_i\),爆炸半徑是 \(R_i\),當一個炸彈爆炸時,若是另外一個炸彈所在位置\(X_j\) 知足: \(X_i-R_i\leq X_j \leq X_i+R_i\) ,那麼,該炸彈也會被引爆。 如今,請你幫忙計算一下,先把第\(i\)個炸彈引爆,將引爆多少個炸彈呢?ui

答案對\(1000000007\)取模spa

輸入格式

第一行,一個數字\(N\),表示炸彈個數。 第\(2\)~\(N+1\)行,每行\(2\)個數字,表示\(X_i ,R_i\),保證\(X_i\)嚴格遞增code

說明/提示

\(N \leq 500000 , -10^{18} \leq X_i \leq 10^{18},0 \leq R_i \leq 2 \times 10^{18}\)blog

題解

這道題要求的其實就是引爆每個炸彈所附加的引爆炸彈的數量get

先想最暴力的方法,顯然就是對每個點進行搜索,算出他所能到達的點的數量it

那麼就能夠對可以互相到達的兩個點連邊,統計答案的時候直接進行DFS。ast

時間複雜度\(O(n^2)\),空間複雜度也是\(O(n^2)\),顯然過不了

咱們嘗試找一下這個題目特殊的性質:每個點所能覆蓋到的點在一個區間裏,也就是說這個點所能連到的點都要在這個區間裏。換句話說,對於這個區間裏的每個點都要連一條邊。而存貯這些邊是致使空間爆炸的緣由,遍歷這些邊則是致使時間爆炸的緣由。那麼要優化複雜度,顯然要優化存儲邊的方式。

接下來考慮怎麼優化?

先來看幾張圖
11

咱們如今要把點0向1~5這些點連邊,一共連了五條邊

可是若是咱們把這些點放到線段樹上

1

發現只須要把0點向區間1~5連一條邊就好了

這樣就大大減小了連邊的數量

能夠證實,給n個點連邊,經過線段樹優化的方式,複雜度由\(O(n^2)\)優化到了\(O(nlogn)\)(證實略)​

連完邊以後,能夠發現可以相互到達的點必定在同一個強連通份量裏面,因而直接跑tarjan縮點,記錄每個強聯通份量裏面的節點數量,表明這個強連通份量裏面的點可以到達的點的個數。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read()
{
    ll ans=0;char last=' ',c=getchar();
    while(c<'0'||c>'9') last=c,c=getchar();
    while(c>='0'&&c<='9') ans=(ans<<3)+(ans<<1)+c-'0',c=getchar();
    if(last=='-') ans=-ans;
    return ans;
}
const ll N=500050,mod=1e9+7;
ll head[N<<4],Head[N<<4],ecnt,Ecnt;
struct edge{ll to,nxt;}edg[N<<5],Edg[N<<5];
inline void add_edge(int u,int v)
{
    edg[++ecnt].to=v;
    edg[ecnt].nxt=head[u];
    head[u]=ecnt;
}

inline void Add_edge(int u,int v)
{
    Edg[++Ecnt].to=v;
    Edg[Ecnt].nxt=Head[u];
    Head[u]=Ecnt;
}

ll n,node;
ll X[N],R[N];
ll id[N<<4],w[N<<4];

void build(int cnt,int l,int r)
{
    if(l==r) 
    {
        id[l]=cnt;node=max(node,ll(cnt));w[cnt]=1;
        return;
    }
    ll mid=l+r>>1;
    build(cnt<<1,l,mid);build(cnt<<1|1,mid+1,r);
    add_edge(cnt,cnt<<1),add_edge(cnt,cnt<<1|1);
}

void add(int cnt,int l,int r,int nl,int nr,int x)
{
    if(nl<=l&&nr>=r)
    {
        add_edge(x,cnt);
        return;
    }
    ll mid=l+r>>1;
    if(nl<=mid) add(cnt<<1,l,mid,nl,nr,x);
    if(nr>mid) add(cnt<<1|1,mid+1,r,nl,nr,x);
}

ll dfn[N<<4],low[N<<4],ind,scc[N<<4],cnt,in[N<<4],s[N<<4],top,W[N<<4];
void tarjan(int x)
{
    low[x]=dfn[x]=++ind;
    s[top++]=x;
    in[x]=1;
    for(int i=head[x];i;i=edg[i].nxt)
    {
        int v=edg[i].to;
        if(!dfn[v])
        {
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(in[v]) low[x]=min(low[x],dfn[v]);
    }
    if(dfn[x]==low[x])
    {
        cnt++;
        while(s[top]!=x)
        {
            top--;
            in[s[top]]=0;
            scc[s[top]]=cnt;
            W[cnt]+=w[s[top]];
        }
    }
}

void rebuild()
{
    for(int u=1;u<=node;u++)
    {
        for(int i=head[u];i;i=edg[i].nxt)
        {
            int v=edg[i].to;
            if(scc[u]==scc[v]) continue;
            Add_edge(scc[u],scc[v]);
        }
    }
}

queue<int> Q;
ll f[N<<4],vis[N<<4];

void dfs(int u)
{
    if(f[u]) return;
    vector<int> a;
    f[u]=W[u];
    for(int i=Head[u];i;i=Edg[i].nxt)
    {
        int v=Edg[i].to;
        dfs(v);a.push_back(v);
    }
    for(int i=0;i<a.size();i++)
    {
        if(vis[a[i]]==u)continue;
        vis[a[i]]=u;f[u]+=f[a[i]];
    }
}

ll ans=0;

int main()
{
    n=read();
    for(int i=1;i<=n;i++) X[i]=read(),R[i]=read();
    build(1,1,n);
    for(int i=1;i<=n;i++)
    {
        ll l,r;
        l=lower_bound(X+1,X+1+n,X[i]-R[i])-X;
        r=upper_bound(X+1,X+1+n,X[i]+R[i])-X-1;
        add(1,1,n,l,r,id[i]);
    }
    for(int i=1;i<=node;i++) if(!dfn[i]) tarjan(i);
    rebuild();
    for(int i=1;i<=cnt;i++) dfs(i);
    for(int i=1;i<=n;i++) ans=(ans+i*f[scc[id[i]]])%mod;
    cout<<ans;
}
相關文章
相關標籤/搜索