[SNOI2017]炸弹
Link
Solution
一个显然的做法是把一个点能到达的所有点连上边,然后缩点,形成一个 DAG,在 DAG 上统计能到达的点的个数,转换成可达性统计,用 bitset 随便维护一下就可以了。(大概能得 30 分?)
这个做法有两个问题,边的数量级是 \(O(n^2)\) 的,bitset 的空间复杂度也是 \(O(\frac{n^2}{32})\) 的。第一个问题的解决方法就是建边优化。考虑到连的边是一个区间,所以直接线段树建图就可以了。同样利用这个区间的性质。对任意三个点 \(u<v<p\),那么若 \(p\) 能到达 \(u\) 那一定能到达 \(v\)。所以对于每一个点,只需要统计它能到达的区间的左右端点即可,一遍拓扑排序就可以解决。
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
#define N 1000007
#define int long long
#define ll long long
#define lid id<<1
#define rid id<<1|1
#define Mod 1000000007
#define re register
inline ll read(){
ll x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
struct E{
int next,to;
}e[N*20];
int seg[N],head[N*4],cnt=0;
int dfn[N*4],c[N*4],low[N*4],sta[N*4],top=0,ti=0,scc=0;
int X[N*20],Y[N*20],n,L[N*4],R[N*4];
ll a[N],r_[N];
inline void add(int id,int to){
e[++cnt]=(E){head[id],to};
X[cnt]=id,Y[cnt]=to;
head[id]=cnt;
}
inline void build(int id,int lf,int rf){
if(lf==rf) seg[lf]=id;
else{
int mid=(lf+rf)>>1;
build(lid,lf,mid);
build(rid,mid+1,rf);
add(id,lid),add(id,rid);
}
}
int l,r,pos;
inline void link(int id,int lf,int rf){
if(l<=lf&&rf<=r){
if(pos==id) return ;
add(pos,id);
}else{
int mid=(lf+rf)>>1;
if(l<=mid) link(lid,lf,mid);
if(r>mid) link(rid,mid+1,rf);
}
}
inline void tarjan(int u){
dfn[u]=low[u]=++ti;
sta[++top]=u;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}else if(!c[v])
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
scc++;
int y;
do{
y=sta[top--];
c[y]=scc;
}while(y!=u);
}
}
bool vis[N*4];
inline void dfs(int u){
vis[u]=1;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(!vis[v]) dfs(v);
if(!L[v]) continue;
L[u]=min(L[u],L[v]);
R[u]=max(R[u],R[v]);
}
}
struct Node{
ll x,r;
bool operator <(const Node &X) const{return x<X.x;}
}A[N];
void build2(int id,int lf,int rf){
L[c[id]]=min(L[c[id]],lf);
R[c[id]]=max(R[c[id]],rf);
if(lf!=rf){
int mid=(lf+rf)>>1;
build2(lid,lf,mid);
build2(rid,mid+1,rf);
}
}
signed main(){
// freopen("Alphawithomega.in","r",stdin);
// freopen("Alphawithomega.out","w",stdout);
n=read();
build(1,1,n);
for(re int i=1;i<=n;i++) A[i].x=read(),A[i].r=read();
sort(A+1,A+1+n);
for(re int i=1;i<=n;i++) a[i]=A[i].x,r_[i]=A[i].r;
for(re int i=1;i<=n;i++){
if(!r_[i]) continue;
l=lower_bound(a+1,a+1+n,a[i]-r_[i])-a;
r=upper_bound(a+1,a+1+n,a[i]+r_[i])-a-1;
if(l<1) l=1; if(r>n) r=n; pos=seg[i];
link(1,1,n);
}
for(re int i=1;i<=seg[n];i++)
if(!dfn[i]) tarjan(i);
memset(head,0,sizeof(head));
int tmp=cnt; cnt=0;
for(re int i=1;i<=tmp;i++)
if(c[X[i]]!=c[Y[i]]) add(c[X[i]],c[Y[i]]);
memset(L,0x3f3f3f3f,sizeof(L));
build2(1,1,n);
ll ret=0,sum=0;
for(re int i=1;i<=scc;i++) dfs(i);
for(re ll i=1;i<=n;i++)
ret=(ret+i*(R[c[seg[i]]]-L[c[seg[i]]]+1)%Mod)%Mod;
printf("%lld",ret);
}
/*
4
1 1
5 1
6 5
15 15
*/