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; }