BZOJ5017 [SNOI2017]炸弹 - 线段树优化建图+Tarjan
Solution
一个点向一个区间内的所有点连边, 可以用线段树优化建图来优化 : 前置技能传送门
然后就得到一个有向图, 一个联通块内的炸弹可以互相引爆, 所以进行缩点变成$DAG$
然后拓扑排序。 由于一次引爆的炸弹 一定是一个连续的区间内, 所以只需要记录左右边界, 并将左右边界转移给能到达它的联通块。
没写手工栈一直RE的我心里$mmp$啊。 为什么网上的题解都不写手工栈$QAQ$
Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rd read() 5 #define ll long long 6 using namespace std; 7 8 const int N = 2000010; 9 const int mod = 1e9 + 7; 10 const ll inf = 3e18; 11 12 int n, lscnt; 13 int head[N], tot; 14 int Head[N], Tot; 15 int low[N], dfn[N], dfstot; 16 int c[N], col; 17 ll minn[N], maxn[N], ls[N]; 18 int st[N], tp; 19 bool vis[N], inq[N]; 20 21 struct edge { 22 int nxt, to; 23 }e[N * 10], E[N * 10]; 24 25 struct boom { 26 ll pos, r; 27 }bo[N]; 28 29 ll read() { 30 ll X = 0, p = 1; char c = getchar(); 31 for (; c > '9' || c < '0'; c = getchar()) 32 if (c == '-') p = -1; 33 for (; c >= '0' && c <= '9'; c = getchar()) 34 X = X * 10 + c - '0'; 35 return X * p; 36 } 37 38 void add(int u, int v) { 39 e[++tot].to = v; 40 e[tot].nxt = head[u]; 41 head[u] = tot; 42 } 43 44 void Add(int u, int v) { 45 E[++Tot].to = v; 46 E[Tot].nxt = Head[u]; 47 Head[u] = Tot; 48 } 49 50 namespace SegT { 51 #define mid ((l + r) >> 1) 52 int lc[N], rc[N], root, cnt; 53 54 void build(int &p, int l, int r) { 55 if (l == r) { 56 p = l; return; 57 } 58 p = ++cnt; 59 build(lc[p], l, mid); 60 build(rc[p], mid + 1, r); 61 add(p, lc[p]); add(p, rc[p]); 62 } 63 64 void update(int L, int R, int c, int l, int r, int x) { 65 if (L > R) return; 66 if (L <= l && r <= R) { 67 add(c, x); return; 68 } 69 if (mid >= L) 70 update(L, R, c, l,mid, lc[x]); 71 if (mid < R) 72 update(L, R, c, mid + 1, r, rc[x]); 73 } 74 }using namespace SegT; 75 76 #define R register 77 int stx[N], sti[N], lv, stnt[N]; 78 #define u stx[lv] 79 #define nt stnt[lv] 80 #define i sti[lv] 81 void tarjan(int x) { 82 lv = 1; stx[1] = x; 83 start:; 84 low[u] = dfn[u] = ++dfstot; 85 inq[u] = true; st[++tp] = u; 86 for (i = head[u]; i; i = e[i].nxt) { 87 nt = e[i].to; 88 if (!dfn[nt]) { 89 // tarjan(nt); 90 stx[lv + 1] = nt; 91 lv++; 92 goto start; 93 end:; 94 low[u] = min(low[u], low[nt]); 95 } 96 else if (inq[nt]) low[u] = min(low[u], dfn[nt]); 97 } 98 if (low[u] == dfn[u]) { 99 col++; 100 for (; tp;) { 101 int z = st[tp--]; 102 inq[z] = false; 103 if (z <= n) 104 maxn[col] = max(maxn[col], bo[z].pos + bo[z].r), 105 minn[col] = min(minn[col], bo[z].pos - bo[z].r); 106 c[z] = col; 107 if (z == u) break; 108 } 109 } 110 --lv; 111 if (lv) goto end; 112 } 113 #undef u 114 #undef nt 115 #undef i 116 117 void dfs(int u) { 118 vis[u] = true; 119 for (R int i = Head[u]; i; i = E[i].nxt) { 120 int nt = E[i].to; 121 if (vis[nt] == false) dfs(nt); 122 minn[u] = min(minn[u], minn[nt]); 123 maxn[u] = max(maxn[u], maxn[nt]); 124 } 125 } 126 127 int fdl(ll x) { 128 return lower_bound(ls + 1, ls + 1 + lscnt, x) - ls; 129 } 130 131 int fdr(ll x) { 132 int re = lower_bound(ls + 1, ls + 1 + lscnt, x) - ls; 133 if (ls[re] == x) return re; 134 else return re - 1; 135 } 136 137 int main() 138 { 139 cnt = n = rd; 140 for (R int i = 1; i <= n; ++i) { 141 bo[i].pos = rd; bo[i].r = rd; 142 ls[++lscnt] = bo[i].pos; 143 } 144 sort(ls + 1, ls + 1 + lscnt); 145 lscnt = unique(ls + 1, ls + 1 + lscnt) - ls - 1; 146 build(root, 1, n); 147 for (int i = 1; i <= n; ++i) { 148 int l = fdl(bo[i].pos - bo[i].r), r = fdr(bo[i].pos + bo[i].r), m = fdl(bo[i].pos); 149 update(l, m - 1, m, 1, n, root); 150 update(m + 1, r, m, 1, n, root); 151 } 152 for (int i = 1; i < N; ++i) 153 maxn[i] = -inf, minn[i] = inf; 154 for (int i = 1; i <= cnt; ++i) 155 if (!dfn[i]) tarjan(i); 156 for (int u = 1; u <= cnt; ++u) 157 for (int i = head[u]; i; i = e[i].nxt) { 158 int v = e[i].to; 159 if (c[u] == c[v]) 160 continue; 161 Add(c[u], c[v]); 162 } 163 for (int i = 1; i <= col; ++i) dfs(i); 164 ll ans = 0; 165 for (int i = 1; i <= n; ++i) { 166 int l = fdl(minn[c[i]]), r = fdr(maxn[c[i]]); 167 (ans += 1LL * i * (r - l + 1) % mod) %= mod; 168 } 169 printf("%lld\n", ans); 170 }