题解 P5025 [SNOI2017]炸弹

题面
首先处理出每个炸弹爆炸直接能引爆的炸弹
然后用线段树完成区间加边操作
然后显然我们会考虑算出每个点能到的叶子节点数
然而我们发现并没有很好的算法能完成它

可以发现每个炸弹能直接或间接引爆的炸弹必然是一个区间
于是又考虑搞出这个区间的左右端点

然后就是常规操作:

  1. tarjan缩点
  2. 拓扑序dp(我用记忆化dfs实现)

然后就可以搞了

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
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll x[500010], l[500010], r[500010];
struct edge {
int u, v, nxt;
} e[10000010];
int head[2000010], tot;
inline void addedge(int u, int v) {
e[++tot] = edge{u, v, head[u]};
head[u] = tot;
}
int id[500010];
inline void build(int now, int l, int r) {
if(l == r) {
id[l] = now;
return;
}
int mid = l + r >> 1;
addedge(now, now << 1);
addedge(now, now << 1 | 1);
build(now << 1, l, mid);
build(now << 1 | 1, mid + 1, r);
}
inline void add(int x, int l, int r, int L, int R, int u) {
if(l > R || L > r) return;
if(L <= l && r <= R) {
addedge(u, x);
return;
}
int mid = l + r >> 1;
add(x << 1, l, mid, L, R, u);
add(x << 1 | 1, mid + 1, r, L, R, u);
}
int qid[2000010], mx[2000010], qmx[2000010], qmn[2000010];
int dfn[2000010], qcnt, low[2000010];
int sta[2000010], top, insta[2000010];
int vis[2000010];
inline void dfs(int now) {
if(vis[now]) return;
// printf("%d %d %d\n", now, qmn[now], qmx[now]);
vis[now] = 1;
for(int i = head[now]; i; i = e[i].nxt) {
dfs(e[i].v);
qmn[now] = min(qmn[now], qmn[e[i].v]);
qmx[now] = max(qmx[now], qmx[e[i].v]);
}
// printf("%d %d %d\n", now, qmn[now], qmx[now]);
}
inline void tarjan(int now) {
static int dfsnow = 0;
dfn[now] = low[now] = ++dfsnow;
sta[++top] = now;
insta[now] = 1;
for(int i = head[now]; i; i = e[i].nxt) {
if(!dfn[e[i].v]) tarjan(e[i].v);
if(insta[e[i].v]) low[now] = min(low[now], low[e[i].v]);
}
if(low[now] == dfn[now]) {
++qcnt;
qmx[qcnt] = -1e9;
qmn[qcnt] = 1e9;
do {
qid[sta[top]] = qcnt;
if(mx[sta[top]])
qmx[qcnt] = max(qmx[qcnt], mx[sta[top]]),
qmn[qcnt] = min(qmn[qcnt], mx[sta[top]]);
insta[sta[top]] = 0;
} while(sta[top--] != now);
}
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%lld%lld", x + i, r + i), l[i] = x[i] - r[i], r[i] = x[i] + r[i];
build(1, 1, n);
for(int i = 1; i <= n; i++) mx[id[i]] = i;
for(int i = 1; i <= n; i++) {
l[i] = lower_bound(x + 1, x + 1 + n, l[i]) - x;
r[i] = upper_bound(x + 1, x + 1 + n, r[i]) - x - 1;
// printf("%lld %lld\n", l[i], r[i]);
add(1, 1, n, l[i], r[i], id[i]);
}
tarjan(1);
memset(head, 0, sizeof head);
int lasttot = tot;
tot = 0;
for(int i = 1; i <= lasttot; i++)
if(qid[e[i].u] != qid[e[i].v]) addedge(qid[e[i].u], qid[e[i].v]); //感受一下,它是对的。。。
long long ans = 0;
for(int i = 1; i <= n; i++) {
dfs(qid[id[i]]);
// printf("%d %d\n", qmn[qid[id[i]]], qmx[qid[id[i]]]);
ans = ans + 1ll * (qmx[qid[id[i]]] - qmn[qid[id[i]]] + 1) * i;
}
return cout << ans % 1000000007 << endl, 0;
}