【luogu P5025】炸弹(线段树优化建图)(Tarjan)
炸弹
题目链接:luogu P5025
题目大意
一个横轴让有一些炸弹,每个有爆炸范围,如果一个爆炸时另一个在的它的爆炸范围那那个也会爆炸。
然后问你对于每个炸弹引爆它会使多少炸弹爆炸。
思路
不难看出每个炸弹可以炸到那些我们可以通过左右分别二分 / STL 得到。
然后如果边数允许,我们可以直接连边表示炸了 \(i\) 就会跟着炸 \(j\),然后 Tarjan 缩点跑图得出答案。
但是边数是 \(n^2\) 的。
然后我们考虑它这些给一个区间的点连边如何优化,考虑用线段树。
线段树上一个包含 \(i\sim j\) 的点就代表 \(i\sim j\) 的所有点,朝他连边就是朝里面的所有点都连边。
然后我们在 Tarjan 重建图之后 dfs 一次把下面的范围传递给上面就可以用了。
然后因为要传上来所有 \(i\) 要给 \(i*2,i*2+1\) 连边。
然后直接搞就可以了。
代码
#include
#include
#include
#include
#define ll long long
#define mo 1000000007
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
int n;
ll x[500001], r[500001];
int pl[500001];
vector lnk[500001 << 2], e[500001 << 2];
ll ans;
struct XD_Tree {
int ls[500001 << 2], rs[500001 << 2], MAXN;
void build(int now, int l, int r) {
ls[now] = l; rs[now] = r;
MAXN = max(MAXN, now);
if (l == r) {
pl[l] = now; return ;
}
int mid = (l + r) >> 1;
build(now << 1, l, mid); build(now << 1 | 1, mid + 1, r);
lnk[now].push_back(now << 1); lnk[now].push_back(now << 1 | 1);
}
void connect(int now, int l, int r, int L, int R, int x) {
if (L <= l && r <= R) {
if (now == x) return ;
lnk[x].push_back(now);
return ;
}
int mid = (l + r) >> 1;
if (L <= mid) connect(now << 1, l, mid, L, R, x);
if (mid < R) connect(now << 1 | 1, mid + 1, r, L, R, x);
}
}T;
int dfn[500001 << 2], low[500001 << 2], col[500001 << 2];
int lft[500001 << 2], rght[500001 << 2];
int sta[500001 << 2], tmp, tot;
void tarjan(int now) {
dfn[now] = low[now] = ++tmp;
sta[++sta[0]] = now;
for (int i = 0; i < lnk[now].size(); i++) {
int x = lnk[now][i];
if (!dfn[x]) tarjan(x), low[now] = min(low[now], low[x]);
else if (!col[x]) low[now] = min(low[now], dfn[x]);
}
if (low[now] == dfn[now]) {
col[now] = ++tot;
lft[tot] = T.ls[now]; rght[tot] = T.rs[now];
while (sta[sta[0]] != now) {
col[sta[sta[0]]] = tot;
lft[tot] = min(lft[tot], T.ls[sta[sta[0]]]);
rght[tot] = max(rght[tot], T.rs[sta[sta[0]]]);
sta[0]--;
}
sta[0]--;
}
}
bool in[500001 << 2];
void dfs(int now) {
in[now] = 1;
for (int i = 0; i < e[now].size(); i++) {
int x = e[now][i];
if (in[x]) {
lft[now] = min(lft[now], lft[x]);
rght[now] = max(rght[now], rght[x]);
}
else {
dfs(x);
lft[now] = min(lft[now], lft[x]);
rght[now] = max(rght[now], rght[x]);
}
}
}
int query(int x) {
x = col[pl[x]];
return rght[x] - lft[x] + 1;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld %lld", &x[i], &r[i]);
}
x[n + 1] = INF;
T.build(1, 1, n);
for (int i = 1; i <= n; i++) {
int L = lower_bound(x + 1, x + n + 1, x[i] - r[i]) - x;
int R = upper_bound(x + 1, x + n + 1, x[i] + r[i]) - x - 1;
T.connect(1, 1, n, L, R, pl[i]);
T.ls[pl[i]] = L; T.rs[pl[i]] = R;
}
tarjan(1);//线段树一定连通
for (int i = 1; i <= T.MAXN; i++)
for (int j = 0; j < lnk[i].size(); j++) {
if (col[i] != col[lnk[i][j]]) {
e[col[i]].push_back(col[lnk[i][j]]);
}
}
for (int i = 1; i <= tot; i++) {//去重边
sort(e[i].begin(), e[i].end());
unique(e[i].begin(), e[i].end());
}
for (int i = 1; i <= tot; i++)
if (!in[i]) dfs(i);
for (int i = 1; i <= n; i++)
(ans += 1ll * i * query(i) % mo) %= mo;
printf("%lld", ans);
return 0;
}