zoukankan      html  css  js  c++  java
  • bzoj千题计划311:bzoj5017: [Snoi2017]炸弹(线段树优化tarjan构图)

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

    暴力:

    对于每一个炸弹,枚举所有的炸弹,看它爆炸能不能引爆那个炸弹

    如果能,由这个炸弹向引爆的炸弹连单向边

    tarjan所点后拓扑排序

    在拓扑图上倒着统计答案

    可以得到一个炸弹能引爆的编号最小mi和最大的炸弹mx,mx-mi+1就是先引爆这个炸弹一共能引爆的炸弹数

    优化:

    一个炸弹一定是向一段区间连边

    所以用线段树优化,这样向一个区间连边就变成了向一个点连边

    注意存边的空间一定要开的足够大

    还有tarjan缩完点后不一定是一棵树,因为是有向图缩点

    #include<cstdio>
    #include<vector>
    #include<iostream>
    #include<algorithm>
     
    using namespace std;
     
    const int mod=1e9+7;
     
    #define N 500001 
     
    typedef long long LL;
     
    int n;
     
    int id[N*3],num;
    int front[N*3],to[N*21],nxt[N*21],from[N*21],tot;
     
    LL pos[N],rad[N];
     
    int dfn[N*3],low[N*3],tim;
    int st[N*3],top;
    bool vis[N*3];
     
    int cnt;
    int bl[N*3],mi[N*3],mx[N*3];
     
    vector<int>V[N*3];
    int in[N*3];
     
    int q[N*3];
     
    template<typename T>
    void read(T &x)
    {
        x=0; int f=1; char c=getchar();
        while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
        x*=f;
    }
     
    void add(int u,int v)
    {
        to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; from[tot]=u; 
        //printf("%d %d
    ",u,v);
    }
     
    /*void add2(int u,int v)
    {
        to2[++tot2]=v; nxt2[tot2]=front2[u]; front2[u]=tot2;
    }*/
     
    void build(int k,int l,int r)
    {
        id[k]=++num;
        if(l==r)
        {
            add(id[k],l);
            return;
        }
        int mid=l+r>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
        add(id[k],id[k<<1]);
        add(id[k],id[k<<1|1]);
    }
     
    void burst(int k,int l,int r,int opl,int opr,int who)
    {
        if(l>=opl && r<=opr)
        {
            add(who,id[k]);
            return;
        }
        int mid=l+r>>1;
        if(opl<=mid) burst(k<<1,l,mid,opl,opr,who);
        if(opr>mid) burst(k<<1|1,mid+1,r,opl,opr,who);
    }
     
    void init()
    {
        read(n);
        num=n;
        build(1,1,n);
        for(int i=1;i<=n;++i) read(pos[i]),read(rad[i]);
        for(int i=1;i<=n;++i) burst(1,1,n,lower_bound(pos+1,pos+n+1,pos[i]-rad[i])-pos,upper_bound(pos+1,pos+n+1,pos[i]+rad[i])-pos-1,i);
    }
     
    void tarjan(int x)
    {
        dfn[x]=low[x]=++tim;
        st[++top]=x;
        vis[x]=true;
        for(int i=front[x];i;i=nxt[i])
            if(!dfn[to[i]])
            {
                tarjan(to[i]);
                low[x]=min(low[x],low[to[i]]);
            }
            else if(vis[to[i]]) low[x]=min(low[x],dfn[to[i]]);
        int y;
        if(low[x]==dfn[x])
        {
            cnt++;
            mi[cnt]=n+1;
            mx[cnt]=0;  
            while(x!=y)
            {
                y=st[top--];
                vis[y]=false;
                bl[y]=cnt;
                if(y<=n) 
                {
                    mi[cnt]=min(mi[cnt],y);
                    mx[cnt]=max(mx[cnt],y);
                }
            }
        }
    }
     
    void rebuild()
    {
        for(int i=1;i<=tot;++i)
            if(bl[from[i]]!=bl[to[i]]) 
            {
                //add2(bl[from[i]],bl[to[i]]);
                V[bl[from[i]]].push_back(bl[to[i]]);
                in[bl[to[i]]]++;
            }
    }
     
    void topsort()
    {
        int head=1,tail=1;
        for(int i=1;i<=cnt;++i) 
            if(!in[i]) q[tail++]=i;
        int now,siz,t;
        while(head<tail)
        {
            now=q[head++];
            siz=V[now].size();
            for(int i=0;i<siz;++i)
            {
                t=V[now][i];
                in[t]--;
                if(!in[t]) q[tail++]=t;
            }
        }
        for(int i=tail-1;i;--i)
        {
            now=q[i];
            siz=V[now].size();
            for(int i=0;i<siz;++i)
            {
                t=V[now][i];
                mi[now]=min(mi[now],mi[t]);
                mx[now]=max(mx[now],mx[t]);
            }
        }
    }
     
     
    void work()
    {
    //  printf("%d %d
    ",num,tot);
        for(int i=1;i<=num;++i)
            if(!dfn[i]) tarjan(i);
        rebuild();
        topsort();
        int ans=0;
        for(int i=1;i<=n;++i) 
        {
            ans+=1LL*i*(mx[bl[i]]-mi[bl[i]]+1)%mod;
            ans-=ans>=mod ? mod : 0;
        }
        printf("%d",ans);
    }
     
    int main()
    {
        init();
        work();
    }
  • 相关阅读:
    简单工厂模式&工厂方法模式&抽象工厂模式的区别及优缺点及使用场景
    JDK1.8的新特性
    在Button样式中添加EventSetter,理解路由事件
    关于C#低版本升级高版本时,项目中引用Microsoft.Office.Interop.Word,程序提示不存在类型或命名空间名office.
    无法安装或运行此应用程序。该应用程序要求首先在"全局程序集缓存(GAC)"中安装程序集
    C#winform跨窗体传值和调用事件的办法
    C#线程处理:七、线程实列
    C#线程处理:六、线程同步(三)
    C#线程处理:五、线程同步(二)
    C#线程处理:四、线程同步
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8964680.html
Copyright © 2011-2022 走看看