[SNOI2017]炸弹

Solution

一个显然的做法是把一个点能到达的所有点连上边,然后缩点,形成一个 DAG,在 DAG 上统计能到达的点的个数,转换成可达性统计,用 bitset 随便维护一下就可以了。(大概能得 30 分?)
这个做法有两个问题,边的数量级是 \(O(n^2)\) 的,bitset 的空间复杂度也是 \(O(\frac{n^2}{32})\) 的。第一个问题的解决方法就是建边优化。考虑到连的边是一个区间,所以直接线段树建图就可以了。同样利用这个区间的性质。对任意三个点 \(u<v<p\),那么若 \(p\) 能到达 \(u\) 那一定能到达 \(v\)。所以对于每一个点,只需要统计它能到达的区间的左右端点即可,一遍拓扑排序就可以解决。

#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
#define N 1000007
#define int long long
#define ll long long
#define lid id<<1
#define rid id<<1|1
#define Mod 1000000007
#define re register

inline ll read(){
    ll x=0,flag=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

struct E{
    int next,to;
}e[N*20];

int seg[N],head[N*4],cnt=0;
int dfn[N*4],c[N*4],low[N*4],sta[N*4],top=0,ti=0,scc=0;
int X[N*20],Y[N*20],n,L[N*4],R[N*4];
ll a[N],r_[N];

inline void add(int id,int to){
    e[++cnt]=(E){head[id],to};
    X[cnt]=id,Y[cnt]=to;
    head[id]=cnt;
}

inline void build(int id,int lf,int rf){
    if(lf==rf) seg[lf]=id;
    else{
        int mid=(lf+rf)>>1;
        build(lid,lf,mid);
        build(rid,mid+1,rf);
        add(id,lid),add(id,rid);
    }
}

int l,r,pos;
inline void link(int id,int lf,int rf){
    if(l<=lf&&rf<=r){
        if(pos==id) return ;
        add(pos,id);
    }else{
        int mid=(lf+rf)>>1;
        if(l<=mid) link(lid,lf,mid);
        if(r>mid) link(rid,mid+1,rf);
    }
}

inline void tarjan(int u){
    dfn[u]=low[u]=++ti;
    sta[++top]=u;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }else if(!c[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]){
        scc++;
        int y;
        do{
            y=sta[top--];
            c[y]=scc;
        }while(y!=u);
    }
}

bool vis[N*4];
inline void dfs(int u){
    vis[u]=1;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(!vis[v]) dfs(v);
        if(!L[v]) continue;
        L[u]=min(L[u],L[v]);
        R[u]=max(R[u],R[v]);
    }
}

struct Node{
    ll x,r;
    bool operator <(const Node &X) const{return x<X.x;}
}A[N];

void build2(int id,int lf,int rf){
    L[c[id]]=min(L[c[id]],lf);
    R[c[id]]=max(R[c[id]],rf);
    if(lf!=rf){
        int mid=(lf+rf)>>1;
        build2(lid,lf,mid);
        build2(rid,mid+1,rf);
    }
}

signed main(){
//    freopen("Alphawithomega.in","r",stdin);
//    freopen("Alphawithomega.out","w",stdout);
    n=read();
    build(1,1,n);
    for(re int i=1;i<=n;i++) A[i].x=read(),A[i].r=read();
    sort(A+1,A+1+n);
    for(re int i=1;i<=n;i++) a[i]=A[i].x,r_[i]=A[i].r;
    for(re int i=1;i<=n;i++){
        if(!r_[i]) continue;
        l=lower_bound(a+1,a+1+n,a[i]-r_[i])-a;
        r=upper_bound(a+1,a+1+n,a[i]+r_[i])-a-1;
        if(l<1) l=1; if(r>n) r=n; pos=seg[i];
        link(1,1,n);
    }
    for(re int i=1;i<=seg[n];i++)
        if(!dfn[i]) tarjan(i);
    memset(head,0,sizeof(head));
    int tmp=cnt; cnt=0;
    for(re int i=1;i<=tmp;i++)
        if(c[X[i]]!=c[Y[i]]) add(c[X[i]],c[Y[i]]);
    memset(L,0x3f3f3f3f,sizeof(L));
    build2(1,1,n);
    ll ret=0,sum=0;
    for(re int i=1;i<=scc;i++) dfs(i);
    for(re ll i=1;i<=n;i++)
        ret=(ret+i*(R[c[seg[i]]]-L[c[seg[i]]]+1)%Mod)%Mod;
    printf("%lld",ret);
}
/*
4
1 1
5 1
6 5
15 15
*/
posted @ 2020-12-12 11:04  Kreap  阅读(95)  评论(0编辑  收藏  举报