[BZOJ5017] [SNOI2017] 炸弹

题目链接

BZOJ:https://lydsy.com/JudgeOnline/problem.php?id=5017.

LOJ:https://loj.ac/problem/2255.

洛谷:https://www.luogu.org/problemnew/show/P5025.

Solution

这题有一个比较显然的暴力:

  • 对于一个点\(x\),我们二分出\(l,r\)表示\(x\)爆炸可以波及到的点的范围,然后我们从\(x\)\([l,r]\)分别连边。

  • 建完图之后我们\(tarjan\)缩点,然后对于点\(x\)我们记录\(L_x,R_x\)表示\(x\)爆炸考虑连锁反应可以炸到的范围,这个在\(DAG\)\(dp\)就可以了,这个点的答案就是\(R_x-L_x+1\)

那么正解其实就很显然了,由于每次建边都是从一个点连向一个区间,那么直接线段树优化建图就好了。

具体的,我们建一棵\(1\sim n\)的线段树,父亲向儿子连有向边,然后对于\(x\to [l,r]\),我们把\([l,r]\)拆成\(O(\log n)\)个区间,然后我们就只需要建\(O(\log n)\)条边了,然后缩点就直接在这个线段树上跑就好了。

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

#define ll long long 

void read(ll &x) {
    x=0;ll f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

#define lf double
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)

const int maxn = 5e5+10;
const int inf = 1e9;
const lf eps = 1e-8;
const int mod = 1e9+7;

int col,n,bel[maxn<<2],rev[maxn<<2];

struct Graph {
    int head[maxn<<2],tot,deg[maxn<<2],l[maxn<<2],r[maxn<<2],t[maxn<<2],top;
    struct edge{int to,nxt;}e[maxn<<3];

    void ins(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot,deg[v]++;}
    
    void toposort() {
        queue<int > q;
        for(int i=1;i<=col;i++) if(!deg[i]) q.push(i);
        while(!q.empty()) {
            int now=q.front();q.pop();t[++top]=now;
            for(int i=head[now];i;i=e[i].nxt)
                if(!--deg[e[i].to]) q.push(e[i].to);
        }
        reverse(t+1,t+top+1);
        for(int x=1;x<=top;x++) {
            for(int i=head[x];i;i=e[i].nxt)
                l[x]=min(l[x],l[e[i].to]),r[x]=max(r[x],r[e[i].to]);
        }
    }
    
    void solve() {
        int ans=0;
        for(int i=1;i<=n;i++)
            ans=(ans+1ll*i*(r[bel[rev[i]]]-l[bel[rev[i]]]+1)%mod)%mod;
        write(ans);
    }
}G;

ll pos[maxn],R[maxn];
int head[maxn<<2],tot,dfn[maxn<<2],low[maxn<<2],dfn_cnt,sta[maxn<<2],top,vis[maxn<<2],id[maxn<<2];
struct edge{int to,nxt;}e[maxn<<4];

void ins(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot;}

void build(int p,int l,int r) {
    if(l==r) return rev[l]=p,id[p]=l,void();
    ins(p,ls),ins(p,rs);
    build(ls,l,mid),build(rs,mid+1,r);
}

void insert(int p,int l,int r,int x,int y,int t) {
    if(x<=l&&r<=y) return ins(t,p),void();
    if(x<=mid) insert(ls,l,mid,x,y,t);
    if(y>mid) insert(rs,mid+1,r,x,y,t);
}

void tarjan(int x) {
    dfn[x]=low[x]=++dfn_cnt,sta[++top]=x,vis[x]=1;
    for(int v,i=head[x];i;i=e[i].nxt)
        if(!dfn[v=e[i].to]) tarjan(v),low[x]=min(low[x],low[v]);
        else if(vis[v]) low[x]=min(low[x],dfn[v]);
    if(low[x]==dfn[x]) {
        col++;int r;G.l[col]=1e9;
        while(sta[top+1]!=x) {
            bel[r=sta[top--]]=col,vis[r]=0;
            if(id[r]) {
                G.l[col]=min(G.l[col],id[r]);
                G.r[col]=max(G.r[col],id[r]);
            }
        }
    }
}

int main() {
    scanf("%d",&n);build(1,1,n);
    for(int i=1;i<=n;i++) read(pos[i]),read(R[i]);
    for(int i=1;i<=n;i++) {
        int l=lower_bound(pos+1,pos+n+1,pos[i]-R[i])-pos;
        int r=upper_bound(pos+1,pos+n+1,pos[i]+R[i])-pos-1;
        insert(1,1,n,l,r,rev[i]);
    }
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    for(int x=1;x<=n;x++)
        for(int i=head[x];i;i=e[i].nxt)
            if(bel[e[i].to]!=bel[x]) G.ins(bel[x],bel[e[i].to]);
    G.toposort(),G.solve();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/hbyer/p/10641317.html