线段树优化建图

先给一道题: [SNOI2017]炸弹

我们发现,一个炸弹可能会影响左右的一些炸弹,然后左右的炸弹又会去影响其他的炸弹。

最暴力的做法当然是向每一个会影响到的炸弹连边。

边会达到n^2,显然无法承受。

然后我们就可以用到一个叫做线段树优化建图的技巧

图片摘自洛谷题解~

线段树优化建图_第1张图片

这样边数优化到nlog。

这就是线段树优化建图。


然后对于这道题,需要的还有tarjan缩点,逆向拓扑。

代码算是比较好理解。

#include
#define mod 1000000007
#define LL long long
#define N 500003
using namespace std;
LL read()
{
    LL x=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
void print(LL x)
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)print(x/10);
    putchar(x%10+'0');
}
struct EDGE{
    int nextt,to,me;
}w1[20*N],w2[20*N];

LL b[N],a[N],r[N],minn[N*4],maxx[N*4];
int lc[N*4],rc[N*4],stackk[N*4],vis[N*4],id[N*4],di[N*4],low[N*4],dfn[N*4],belong[N*4],in[N*4];
int head1[N*4],head2[N*4];
int ndnum=1;
int pp;
int cur,cnt,top;
int tot1=0,tot2=0;
void add1(int a,int b)
{
    tot1++;
    w1[tot1].nextt=head1[a];
    w1[tot1].me=a;
    w1[tot1].to=b;
    head1[a]=tot1;
}
void add2(int a,int b)
{
    tot2++;
    w2[tot2].nextt=head2[a];
    w2[tot2].me=a;
    w2[tot2].to=b;
    head2[a]=tot2;
}
void build(int k,int l,int r)
{
    pp=max(pp,k);
    if(l==r){id[l]=k;di[k]=l;return;}
    int mid=(l+r)>>1;
    build(lc[k]=++ndnum,l,mid);
    build(rc[k]=++ndnum,mid+1,r);
    add1(k,lc[k]);add1(k,rc[k]);
}
void update(int k,int L,int R,int l,int r,int x)
{
    if(L==l&&R==r){add1(x,k);return;}
    int mid=(L+R)>>1;
    if(l>mid)update(rc[k],mid+1,R,l,r,x);
    else if(r<=mid)update(lc[k],L,mid,l,r,x);
    else
    {
        update(lc[k],L,mid,l,mid,x);
        update(rc[k],mid+1,R,mid+1,r,x);
    }
}

void tarjan(int x)
{
    low[x]=dfn[x]=++cur;
    stackk[++top]=x;
    vis[x]=1;
    for(int i=head1[x];i;i=w1[i].nextt)
    {
        int id=w1[i].to;
        if(!dfn[id])tarjan(id),low[x]=min(low[x],low[id]);
        else if(vis[id])low[x]=min(low[x],dfn[id]);
    }
    if(low[x]==dfn[x])
    {
        cnt++;
        int t;
        minn[cnt]=4e18;
        maxx[cnt]=-4e18;
        do{
            t=stackk[top--];
            vis[t]=0;
            belong[t]=cnt;
            if(di[t])
            {
                minn[cnt]=min(minn[cnt],a[di[t]]-r[di[t]]);
                maxx[cnt]=max(maxx[cnt],a[di[t]]+r[di[t]]);
            }
        }while(t!=x); 
    }
}
queue<int>q;
int main()
{
    int n=read();
    for(int i=1;i<=n;++i)
      b[i]=a[i]=read(),r[i]=read();
    build(1,1,n);
    sort(b+1,b+1+n);
    
    int num=unique(b+1,b+1+n)-(b+1);
    for(int i=1;i<=n;++i)
    {
        int x=lower_bound(b+1,b+1+num,a[i]-r[i])-b;
        int y=upper_bound(b+1,b+1+num,a[i]+r[i])-b-1;
        update(1,1,n,x,y,id[i]);
    }
    for(int i=1;i<=pp;++i)
      if(!dfn[i])tarjan(i);
    for(int i=1;i<=tot1;++i)
    {
        if(belong[w1[i].me]==belong[w1[i].to])continue;
        add2(belong[w1[i].to],belong[w1[i].me]);//逆向拓扑 
        in[belong[w1[i].me]]++;
    }
    while(!q.empty())q.pop();
    for(int i=1;i<=cnt;++i)
      if(!in[i])q.push(i);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=head2[x];i;i=w2[i].nextt)
        {
            int v=w2[i].to;
            in[v]--;
            if(!in[v])q.push(v);
            minn[v]=min(minn[v],minn[x]);
            maxx[v]=max(maxx[v],maxx[x]);
        }
    }
    LL ans=0;
    for(int i=1;i<=n;++i)
    {
        int l=lower_bound(b+1,b+1+num,minn[belong[id[i]]])-b;
        int r=upper_bound(b+1,b+1+num,maxx[belong[id[i]]])-b-1;//注意是id[i],minn/maxx是存在对应标号里的 
        LL res=(LL)(r-l+1)*i%mod;
        ans=(LL)(ans+res)%mod;
    }
    printf("%lld\n",ans);
}
/*
*/
View Code

 

你可能感兴趣的:(线段树优化建图)