【题解】[SNOI2017]炸弹
\(\text{Solution:}\)
我们发现也就是每次将一个点往周围的点连边然后求每个点能到达的节点数。
点对区间连边,考虑线段树优化建图。
建完之后跑缩点,然后我们发现不会 DAG 上求这个问题。
但是我们又可以发现,我们可以在缩点的时候求出每个点往左往右能到达的最远点。
这样就可以直接获得答案了。
#include<bits/stdc++.h>
using namespace std;
typedef double db;
#define int long long
const int mod=1e9+7;
const db eps=1e-14;
inline int Max(int x,int y){return x>y?x:y;}
inline int Min(int x,int y){return x<y?x:y;}
inline db Max(db x,db y){return x-y>eps?x:y;}
inline db Min(db x,db y){return x-y<eps?x:y;}
inline int Add(int x,int y,int M=mod){return (x+y)%M;}
inline int Mul(int x,int y,int M=mod){return 1ll*x*y%M;}
inline int Dec(int x,int y,int M=mod){return (x-y+M)%M;}
inline int Abs(int x){return x<0?-x:x;}
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline int read(){
int s=0,w=1;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
inline void write(int x){
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
}
inline int qpow(int x,int y){
int res=1;
while(y){
if(y&1)res=Mul(res,x);
x=Mul(x,x);y>>=1;
}
return res;
}
typedef pair<int,int> pr;
#define fi first
#define se second
#define mk make_pair
#define pb emplace_back
#define poly vector<int>
const int N=4e6+10;
namespace Refined_heart{
int tot,pos[N][2],ls[N],rs[N],rt,node;
int head[N],tcnt,n,lb[N],rb[N],siz[N];
int vis[N],dfn[N],low[N],st[N],top;
int inst[N],dfstime,scc,col[N],lq[N],rq[N];
struct E{
int nxt,to,pre;
E(int xx=0,int yy=0,int zz=0){nxt=xx,to=yy,pre=zz;}
}e[N];
struct boom{
int x,r;
boom(int xx=0,int yy=0){x=xx;r=yy;}
}p[N];
inline void link(int x,int y,int p){
e[++tcnt]=E(head[x],y,p);
head[x]=tcnt;
}
E eg[N];
int Head[N],Tcnt;
inline void Link(int x,int y){
eg[++Tcnt]=E(Head[x],y);
Head[x]=Tcnt;
}
void tarjan(int x){
dfn[x]=low[x]=++dfstime;
st[++top]=x;inst[x]=1;
for(int i=head[x];i;i=e[i].nxt){
int j=e[i].to;
if(!dfn[j]){
tarjan(j);
low[x]=Min(low[x],low[j]);
}
else if(inst[j])low[x]=Min(low[x],dfn[j]);
}
if(low[x]==dfn[x]){
int y=0;++scc;
while(y=st[top--]){
if(y<=n)siz[scc]++;
col[y]=scc;
inst[y]=0;
if(x==y)break;
}
}
}
void build(int &x,int l,int r){
x=++node;
if(l==r){
pos[x][0]=pos[x][1]=l;
return;
}
int mid=(l+r)>>1;
build(ls[x],l,mid);
build(rs[x],mid+1,r);
pos[x][0]=++tot;pos[x][1]=++tot;
link(pos[x][1],pos[ls[x]][1],pos[x][1]);
link(pos[x][1],pos[rs[x]][1],pos[x][1]);
link(pos[ls[x]][0],pos[x][0],pos[ls[x]][0]);
link(pos[rs[x]][0],pos[x][0],pos[rs[x]][0]);
}
void Edge(int x,int L,int R,int l,int r,int cur,int opt){
if(L>=l&&R<=r){
if(opt==0)link(pos[x][0],cur,pos[x][0]);
else link(cur,pos[x][1],cur);
return;
}
int mid=(L+R)>>1;
if(l<=mid)Edge(ls[x],L,mid,l,r,cur,opt);
if(mid<r)Edge(rs[x],mid+1,R,l,r,cur,opt);
}
void findpos(int pos){
int l=1,r=pos;
while(l<=r){
int mid=(l+r)>>1;
if(p[pos].x-p[pos].r>p[mid].x)l=mid+1;
else r=mid-1,lb[pos]=mid;
}
l=pos,r=n;
while(l<=r){
int mid=(l+r)>>1;
if(p[pos].x+p[pos].r<p[mid].x)r=mid-1;
else l=mid+1,rb[pos]=mid;
}
}
int V[N];
void dfs(int x){
V[x]=1;
for(int i=Head[x];i;i=eg[i].nxt){
int j=eg[i].to;
if(V[j]){
lq[x]=Min(lq[x],lq[j]);
rq[x]=Max(rq[x],rq[j]);
continue;
}
dfs(j);
lq[x]=Min(lq[x],lq[j]);
rq[x]=Max(rq[x],rq[j]);
}
}
void solve(){
freopen("in.txt","r",stdin);
n=read();
for(int i=1;i<=n;++i){
int u=read(),v=read();
p[i]=boom(u,v);
}
sort(p+1,p+n+1,[&](boom x,boom y){return x.x<y.x;});
for(int i=1;i<=n;++i)findpos(i);
tot=n;build(rt,1,n);
for(int i=1;i<=n;++i)Edge(rt,1,n,lb[i],rb[i],i,1);
for(int i=1;i<=n;++i)if(!dfn[i])tarjan(i);
for(int i=1;i<=tcnt;++i){
int u=e[i].pre,v=e[i].to;
int cu=col[u],cv=col[v];
if(cu==cv)continue;
Link(cu,cv);
}
memset(lq,0x3f,sizeof lq);
memset(rq,-0x3f,sizeof rq);
for(int i=1;i<=n;++i)lq[col[i]]=Min(lq[col[i]],i),rq[col[i]]=Max(rq[col[i]],i);
for(int i=1;i<=scc;++i)if(!V[i])dfs(i);
int ans=0;
for(int i=1;i<=n;++i){
int pos=col[i];
ans=Add(ans,Mul(i,rq[pos]-lq[pos]+1));
}
printf("%lld\n",ans);
}
}
signed main(){
Refined_heart::solve();
return 0;
}