P5025 [SNOI2017]炸弹 题解
P5025 [SNOI2017]炸弹
在数轴上给定n个炸弹,每个炸弹有位置和爆炸半径,如果爆炸半径内有别的炸弹,那么爆炸时就会引爆它们,依次求出从某个炸弹起爆会引起多少个炸弹爆炸.\(n \leq 5*10^5\)
容易发现,本题与之前的CF1158C异曲同工.只是本题更为直观.
考虑二分查找出每个炸弹起爆能引发的爆炸区间,从该炸弹向引爆的炸弹连边,最后dfs过程中统计能到达的结点中最左和最右在哪即可.
由于n较大,连边复杂度过高,考虑线段树优化连边.显然连边之后,对于每个强联通分量,引爆其中任何一个都是引爆整个强联通分量,使用tarjan在线段树上缩点建新图,在新图上跑dfs即可.
思维难度不大,注意代码细节即可:
-
初始化不能只到n,原因在于线段树缩点后点数可能依然多于原图(卡了半天).
-
数字较大,注意long long 和取模.
-
dfs时注意一次性从前后更新所有经过的结点,而不是依次dfs,那样复杂度就boom了
#include <iostream> #include <cstdio> #include <algorithm> #include <map> #include <cstring> #include <assert.h> using namespace std; const long long N = 4 * 500005, INF = 0x3f3f3f3f3f3f3f3f, Mod = 1000000007; long long n, sum, tot, scc, top, ll, rr, totseg; long long head1[N], cnt1, head2[N], cnt2, loc[N], range[N], pos[N], dfn[N], low[N], stack[N], in_sta[N], bel[N], L[N], R[N], vis[N]; map<long long, long long> m[N]; struct point { long long l, r; } p[N * 4]; struct edge { long long to, next; } e1[N * 4], e2[N * 4]; void add1(long long u, long long v) { e1[++cnt1].to = v; e1[cnt1].next = head1[u]; head1[u] = cnt1; } void add2(long long u, long long v) { e2[++cnt2].to = v; e2[cnt2].next = head2[u]; head2[u] = cnt2; } void build(long long k, long long l, long long r) { p[k] = (point){l, r}; totseg = max(totseg, k); if (l == r) { pos[l] = k; return; } long long mid = (l + r) / 2; build(k * 2, l, mid); build(k * 2 + 1, mid + 1, r); add1(k, k * 2); add1(k, k * 2 + 1); } void range_add(long long k, long long l, long long r, long long u) { if (ll <= l && rr >= r) { if (k == u) return; add1(u, k); return; } long long mid = (l + r) / 2; if (ll <= mid) range_add(k * 2, l, mid, u); if (rr > mid) range_add(k * 2 + 1, mid + 1, r, u); } void tarjan(long long k) { dfn[k] = low[k] = ++tot; stack[++top] = k; in_sta[k] = 1; for (long long i = head1[k]; i; i = e1[i].next) { long long v = e1[i].to; if (!dfn[v]) { tarjan(v); low[k] = min(low[k], low[v]); } else if (in_sta[v]) low[k] = min(low[k], dfn[v]); } if (low[k] == dfn[k]) { scc++; while (stack[top + 1] != k) { bel[stack[top]] = scc; in_sta[stack[top]] = 0; L[scc] = min(L[scc], p[stack[top]].l); R[scc] = max(R[scc], p[stack[top]].r); top--; } } } void dfs(long long k) { vis[k] = 1; for (long long i = head2[k]; i; i = e2[i].next) { long long v = e2[i].to; if (vis[v]) { L[k] = min(L[k], L[v]); R[k] = max(R[k], R[v]); //前驱也可以更新后继 continue; } dfs(v); L[k] = min(L[k], L[v]); R[k] = min(R[k], R[v]); //后继更新前驱 } } void build_new_graph() { for (long long i = 1; i <= totseg; i++) for (long long j = head1[i]; j; j = e1[j].next) { long long v = e1[j].to; if (bel[i] != bel[v] && !m[bel[i]][bel[v]]) { m[bel[i]][bel[v]] = 1; add2(bel[i], bel[v]); } } } int main() { ios::sync_with_stdio(false); cin >> n; assert(n <= 500000); for (long long i = 1; i <= n; i++) { cin >> loc[i] >> range[i]; L[i] = INF; } memset(L, 0x3f, sizeof(L)); loc[n + 1] = INF; build(1, 1, n); for (long long i = 1; i <= n; i++) { if (!range[i]) continue; ll = lower_bound(loc + 1, loc + 1 + n, loc[i] - range[i]) - loc; rr = upper_bound(loc + 1, loc + 1 + n, loc[i] + range[i]) - loc - 1; range_add(1, 1, n, pos[i]); p[pos[i]] = (point){ll, rr}; } tarjan(1); build_new_graph(); for (long long i = 1; i <= scc; i++) if (!vis[i]) dfs(i); for (long long i = 1; i <= n; i++) sum = (sum + ((long long)(R[bel[pos[i]]] - L[bel[pos[i]]] + 1) % Mod * i) % Mod) % Mod; cout << sum % Mod << endl; }
本文来自博客园,作者:Kinuhata,转载请注明原文链接:https://www.cnblogs.com/KinuhataSaiai/p/15517010.html