【luogu P5025】炸弹(线段树优化建图)(Tarjan)

炸弹

题目链接:luogu P5025

题目大意

一个横轴让有一些炸弹,每个有爆炸范围,如果一个爆炸时另一个在的它的爆炸范围那那个也会爆炸。
然后问你对于每个炸弹引爆它会使多少炸弹爆炸。

思路

不难看出每个炸弹可以炸到那些我们可以通过左右分别二分 / STL 得到。
然后如果边数允许,我们可以直接连边表示炸了 i 就会跟着炸 j,然后 Tarjan 缩点跑图得出答案。
但是边数是 n2 的。

然后我们考虑它这些给一个区间的点连边如何优化,考虑用线段树。
线段树上一个包含 ij 的点就代表 ij 的所有点,朝他连边就是朝里面的所有点都连边。
然后我们在 Tarjan 重建图之后 dfs 一次把下面的范围传递给上面就可以用了。

然后因为要传上来所有 i 要给 i2,i2+1 连边。
然后直接搞就可以了。

代码

#include<cstdio> #include<vector> #include<iostream> #include<algorithm> #define ll long long #define mo 1000000007 #define INF 0x3f3f3f3f3f3f3f3f using namespace std; int n; ll x[500001], r[500001]; int pl[500001]; vector <int> 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; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P5025.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(45)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示