[ 线段树 tarjan缩点 ] [ SNOI2017 ] BZOJ5017

一个暴力的思想是每个点向能引爆它的点连边,求出强联通分量统计答案。但这样是 O(n2) 的。然后线段树优化建图就好了。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void Read(int& x){
    char c=nc(),b=1;
    for(;c<'0'||c>'9';c=nc())if(c=='-')b=-1;
    for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc());x*=b;
}
typedef long long ll;
inline void Read(ll& x){
    char c=nc(),b=1;
    for(;c<'0'||c>'9';c=nc())if(c=='-')b=-1;
    for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc());x*=b;
}
const int N=500010;
const int P=1000000007;
const int M=7000010;
int ls[M],rs[M],num;
int k,n,m;
int h[M],nx[M],t[M],tc,tot;
int dfn[M],low[M],f[M],cnt;
int st[M],top;
int dp[M];
int Rt,q[M],l,r;
int d[M],H[M],Ans;
int L[M],R[M];
int mn[N],mx[N];
ll a[N],b[N];
bool vis[M];
inline void Add(int x,int y){
    t[++tot]=y;nx[tot]=h[x];h[x]=tot;
}
inline void Add2(int x,int y){
    t[++tot]=y;nx[tot]=H[x];H[x]=tot;d[y]++;
}
void Update(int& x,int l,int r,int y,int z){
    if(!x)x=++num;
    if(l==r){
        Add(x,z);
        return;
    }
    int Mid=l+r>>1;
    if(y<=Mid)Update(ls[x],l,Mid,y,z);else Update(rs[x],Mid+1,r,y,z);
}
void Query(int x,int l,int r,int L,int R,int y){
    if(!x||l>R||r<L)return;
    if(l>=L&&r<=R){
        Add(y,x);
        return;
    }
    int Mid=l+r>>1;
    Query(ls[x],l,Mid,L,R,y);Query(rs[x],Mid+1,r,L,R,y);
}
inline void Up(int x,int y){
    if(y<=n){
        L[x]=min(L[x],y);
        R[x]=max(R[x],y);
    }
}
void Dfs(int x){
    dfn[x]=low[x]=++tc;st[++top]=x;
    for(int i=h[x];i;i=nx[i]){
        int v=t[i];
        if(!dfn[v]){
            Dfs(v);
            low[x]=min(low[x],low[v]);
        }else if(!f[v])low[x]=min(low[x],dfn[v]);
    }
    if(low[x]==dfn[x]){
        cnt++;
        while(st[top]!=x)Up(cnt,st[top]),f[st[top--]]=cnt;
        f[x]=cnt;top--;Up(cnt,x);
    }
}
void Solve(int x){
    vis[x]=1;
    for(int i=H[x];i;i=nx[i]){
        int v=t[i];
        if(!vis[v])Solve(v);
        R[x]=max(R[x],R[v]);L[x]=min(L[x],L[v]);
    }
}
int main(){
    Read(n);num=n;
    for(int i=1;i<=n;i++)Read(a[i]),Read(b[i]),Update(Rt,1,n,i,i);
    for(int i=1;i<=n;i++)mn[i]=lower_bound(a+1,a+n+1,a[i]-b[i])-a,mx[i]=(upper_bound(a+1,a+n+1,a[i]+b[i])-a)-1;
    for(int i=1;i<=n;i++)Query(Rt,1,n,mn[i],mx[i],i);
    for(int i=n+1;i<=num;i++){
        if(ls[i])Add(i,ls[i]);
        if(rs[i])Add(i,rs[i]);
    }
    memset(L,127,sizeof(L));
    for(int i=1;i<=num;i++)
    if(!dfn[i])Dfs(i);
    for(int i=1;i<=num;i++)
    for(int j=h[i];j;j=nx[j])
    if(f[i]!=f[t[j]])Add2(f[i],f[t[j]]);
    for(int i=1;i<=cnt;i++)
    if(!vis[i])Solve(i);
    for(int i=1;i<=n;i++)
    Ans=(Ans+1ll*i*(R[f[i]]-L[f[i]]+1))%P;
    cout<<Ans<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值