BZOJ5017 Snoi2017炸弹(线段树+强连通分量+缩点+传递闭包)

  容易想到每个炸弹向其能引爆的炸弹连边,tarjan缩点后bitset传递闭包。进一步发现每个炸弹能直接引爆的炸弹是一段连续区间,于是线段树优化建图即可让边的数量降至O(nlogn)。再冷静一下由于能间接引爆的炸弹也是一段连续区间,传递闭包时只要记录可达点的左右端点即可。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 500010
#define M N<<2
#define ll long long
#define P 1000000007
ll read()
{
    ll x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
int n,p[M],dfn[M],low[M],stk[M],Set[M],L[M],R[M],root,t,cnt,top,tot,ans=0;
bool flag[M];
ll a[N],b[N];
struct data{int to,nxt;
}edge[M<<2];
struct data2{int l,r;
}tree[M];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
void build(int &k,int l,int r)
{
    if (l==r) {k=l;return;}
    k=++cnt;
    int mid=l+r>>1;
    build(tree[k].l,l,mid);
    build(tree[k].r,mid+1,r);
    addedge(k,tree[k].l),addedge(k,tree[k].r);
}
void add(int k,int l,int r,int x,int y,int p)
{
    if (l==x&&r==y) {addedge(p,k);return;}
    int mid=l+r>>1;
    if (y<=mid) add(tree[k].l,l,mid,x,y,p);
    else if (x>mid) add(tree[k].r,mid+1,r,x,y,p);
    else add(tree[k].l,l,mid,x,mid,p),add(tree[k].r,mid+1,r,mid+1,y,p);
}
void tarjan(int k)
{
    dfn[k]=low[k]=++cnt;stk[++top]=k;flag[k]=1;
    for (int i=p[k];i;i=edge[i].nxt)
    if (!dfn[edge[i].to]) tarjan(edge[i].to),low[k]=min(low[k],low[edge[i].to]);
    else if (flag[edge[i].to]) low[k]=min(low[k],dfn[edge[i].to]);
    if (dfn[k]==low[k])
    {
        tot++;L[tot]=n+1,R[tot]=0;
        while (stk[top]!=k)
        {
            Set[stk[top]]=tot;
            if (stk[top]<=n) L[tot]=min(L[tot],stk[top]),R[tot]=max(R[tot],stk[top]);
            flag[stk[top]]=0;
            top--;
        }
        Set[k]=tot;if (k<=n) L[tot]=min(L[tot],k),R[tot]=max(R[tot],k);flag[k]=0,top--;
    }
}
namespace newgraph
{
    int p[M]={0},degree[M]={0},q[M],t=0;
    struct data{int to,nxt;}edge[M<<1];
    void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;degree[y]++;}
    void topsort()
    {
        int head=0,tail=0;
        for (int i=1;i<=tot;i++) if (!degree[i]) q[++tail]=i;
        while (tail<tot)
        {
            int x=q[++head];
            for (int i=p[x];i;i=edge[i].nxt)
            {
                degree[edge[i].to]--;
                if (!degree[edge[i].to]) q[++tail]=edge[i].to;
            }
        }
    }
    void dp()
    {
        for (int i=tot;i>=1;i--)
        {
            int x=q[i];
            for (int j=p[x];j;j=edge[j].nxt)
            L[x]=min(L[x],L[edge[j].to]),R[x]=max(R[x],R[edge[j].to]);
        }
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj5017.in","r",stdin);
    freopen("bzoj5017.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read();
    for (int i=1;i<=n;i++) a[i]=read(),b[i]=read();
    cnt=n;
    build(root,1,n);
    for (int i=1;i<=n;i++)
    {
        int x,y;
        int l=1,r=i;
        while (l<=r)
        {
            int mid=l+r>>1;
            if (a[i]-b[i]<=a[mid]) x=mid,r=mid-1;
            else l=mid+1;
        }
        l=i,r=n;
        while (l<=r)
        {
            int mid=l+r>>1;
            if (a[i]+b[i]>=a[mid]) y=mid,l=mid+1;
            else r=mid-1;
        }
        add(root,1,n,x,y,i);
    }
    t=cnt;cnt=0;
    for (int i=1;i<=t;i++) if (!dfn[i]) tarjan(i);
    for (int k=1;k<=t;k++)
        for (int i=p[k];i;i=edge[i].nxt)
        if (Set[k]!=Set[edge[i].to]) newgraph::addedge(Set[k],Set[edge[i].to]);
    newgraph::topsort();
    newgraph::dp();
    for (int i=1;i<=n;i++) ans=(ans+1ll*i*(R[Set[i]]-L[Set[i]]+1))%P;
    cout<<ans;
    return 0;
}

 

转载于:https://www.cnblogs.com/Gloid/p/10064740.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值