隐藏
「bzoj3734」「Ontak2013」Miny - 线段树优化建边+Tarjan | Bill Yang's Blog

路终会有尽头,但视野总能看到更远的地方。

0%

「bzoj3734」「Ontak2013」Miny - 线段树优化建边+Tarjan

题目大意

    一个平面内有$N$个地雷,分布在X轴上。每个地雷爆炸后影响的范围是一个贺。
    在这圆内的地雷也会引爆。现在告诉你每个地雷所在坐标$X_i$及爆炸半径$R_i$。请问:这些雷中任一个被引爆后一共会有多少个雷爆炸,注意爆炸是会引起连锁反应的。
    $1\le N\le100000,\left|X_i\right|\le10^{18},0\le R_i\le2\times10^{18}$


题目分析

显然,将每个地雷连向其能炸到的地雷,那么其可到达的结点数即为答案。
但由于$N$太大,直接建边会超时,而会连边的地雷形成了一个区间,故可以使用线段树优化建边。


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include<bits/stdc++.h>

using namespace std;

typedef long long LL;

inline LL Get_Int() {
LL num=0,bj=1;
char x=getchar();
while(!isdigit(x)) {if(x=='-')bj=-1;x=getchar();}
while(isdigit(x)) {num=num*10+x-'0';x=getchar();}
return num*bj;
}

const int maxn=100005,maxp=maxn+(maxn<<2);

struct Point {
LL x,r;
int id;
bool operator < (const Point &b) const {
return x<b.x;
}
} p[maxn];

int n,Maxid,Left[maxp],Right[maxp],ans[maxn];
LL tmp[maxn];
vector<int> edges[maxp],edges2[maxp];

struct Segment_Tree {
struct Tree {
int left,right,id;
Tree(int index=0,int l=0,int r=0):left(l),right(r) {
if(l==r)id=l;
else id=n+index;
Left[id]=l;
Right[id]=r;
}
} tree[maxn<<2];
#define ls index<<1
#define rs index<<1|1
void AddEdge(int x,int y) {
edges[x].push_back(y);
}
void build(int index,int Left,int Right) {
tree[index]=Tree(index,Left,Right);
Maxid=max(Maxid,tree[index].id);
if(Left==Right)return;
int mid=(Left+Right)>>1;
build(ls,Left,mid);
build(rs,mid+1,Right);
AddEdge(tree[index].id,tree[ls].id);
AddEdge(tree[index].id,tree[rs].id);
}
void AddEdge(int index,int Left,int Right,int id) {
if(Right<tree[index].left||Left>tree[index].right)return;
if(Left<=tree[index].left&&Right>=tree[index].right) {
AddEdge(id,tree[index].id);
return;
}
AddEdge(ls,Left,Right,id);
AddEdge(rs,Left,Right,id);
}
} st;

int step=0,top=0,BCC=0,Min[maxp],Max[maxp],InDegree[maxp],Belong[maxp],Dfn[maxp],Lowlink[maxp],Stack[maxp];
bool Instack[maxp];

void Tarjan(int Now) {
Dfn[Now]=Lowlink[Now]=++step;
Stack[++top]=Now;
Instack[Now]=1;
for(int Next:edges[Now]) {
if(!Dfn[Next]) {
Tarjan(Next);
Lowlink[Now]=min(Lowlink[Now],Lowlink[Next]);
} else if(Instack[Next])Lowlink[Now]=min(Lowlink[Now],Dfn[Next]);
}
if(Dfn[Now]==Lowlink[Now]) {
int y;
BCC++;
do {
y=Stack[top--];
Instack[y]=0;
Belong[y]=BCC;
Min[BCC]=min(Min[BCC],Left[y]);
Max[BCC]=max(Max[BCC],Right[y]);
} while(y!=Now);
}
}

void topsort() {
queue<int> Q;
for(int i=1; i<=BCC; i++)if(!InDegree[i])Q.push(i);
while(!Q.empty()) {
int Now=Q.front();
Q.pop();
for(int Next:edges2[Now]) {
Min[Next]=min(Min[Next],Min[Now]);
Max[Next]=max(Max[Next],Max[Now]);
InDegree[Next]--;
if(!InDegree[Next])Q.push(Next);
}
}
}

void Clear() {
step=BCC=0;
for(int i=1; i<=Maxid; i++) {
Min[i]=INT_MAX/2;
Max[i]=InDegree[i]=Dfn[i]=0;
edges[i].clear();
edges2[i].clear();
}
Maxid=0;
}

int main() {
fill(Min,Min+maxp,INT_MAX/2);
int t=Get_Int();
while(t--) {
Clear();
n=Get_Int();
for(int i=1; i<=n; i++) {
p[i].x=Get_Int();
p[i].r=Get_Int();
p[i].id=i;
}
sort(p+1,p+n+1);
st.build(1,1,n);
for(int i=1; i<=n; i++)tmp[i]=p[i].x;
for(int i=1; i<=n; i++) {
int l=lower_bound(tmp+1,tmp+n+1,tmp[i]-p[i].r)-tmp,r=upper_bound(tmp+1,tmp+n+1,tmp[i]+p[i].r)-tmp-1;
if(l!=r)st.AddEdge(1,l,r,i);
}
for(int i=1; i<=Maxid; i++)if(!Dfn[i])Tarjan(i);
for(int Now=1; Now<=Maxid; Now++)
for(int Next:edges[Now]) {
if(Belong[Now]==Belong[Next])continue;
edges2[Belong[Next]].push_back(Belong[Now]);
InDegree[Belong[Now]]++;
}
topsort();
for(int i=1; i<=n; i++)ans[p[i].id]=Max[Belong[i]]-Min[Belong[i]]+1;
for(int i=1; i<=n; i++)printf("%d%c",ans[i],i==n?'\n':' ');
}
return 0;
}
姥爷们赏瓶冰阔落吧~