P5025 [SNOI2017]炸弹
[SNOI2017]炸弹
题目描述
在一条直线上有 个炸弹,每个炸弹的坐标是 ,爆炸半径是 ,当一个炸弹爆炸时,如果另一个炸弹所在位置 满足:
,那么,该炸弹也会被引爆。
现在,请你帮忙计算一下,先把第 个炸弹引爆,将引爆多少个炸弹呢?
答案对 取模
输入格式
第一行,一个数字 ,表示炸弹个数。
第 行,每行两个整数,表示 ,,保证 严格递增。
输出格式
一个数字,表示 炸弹 能引爆的炸弹个数。
样例 #1
样例输入 #1
4 1 1 5 1 6 5 15 15
样例输出 #1
32
提示
【数据范围】
对于 的数据: 。
对于 的数据: 。
对于 的数据: 。
对于 的数据: ,,。
解析
很容易想到将一个炸弹和他能够引爆的炸弹连边建图,但n过大,这样就是一种暴力的做法。采用线段树优化建图的方式,这种方式适用于点向区间连边的问题,可以优化到。在本题中一个炸弹可以引爆一定范围的炸弹,而数据中x又是保证升序的,所以就用线段树连边。用id[]记录n个炸弹在线段树中的编号,连边和跑tarjan缩点时都用线段树中的节点编号。缩好点后再重新建图,对于每个连通块维护他所能引爆的最大范围(注意线段树中的节点也要维护该信息),对于u连通块,跑dfs找到他能到达的所有连通块,更新这个范围。
最后直接计算答案即可。
代码
#include <bits/stdc++.h> using namespace std; #define ll long long #define mid ((l + r) >> 1) #define ls k << 1 #define rs k << 1 | 1 const int N = 5e5 + 10, M = 2e6 + 10, mod = 1e9 + 7; struct node { int l, r; }a[M]; ll x[N], r[N]; int dfn[M], low[M], bel[M]; int id[M], Left[M], Right[M];//id[i]表示i号炸弹在线段树中的编号 int n, nd, cnt, idx, ans; stack<int> s; vector<int> e[M], G[M]; bool vis[M]; void build(int k, int l, int r) { a[k] = {l, r}; nd = max(nd, k);//线段树中最大编号 if (l == r) {id[l] = k; return ;} build(ls, l, mid); build(rs, mid + 1, r); e[k].push_back(ls); e[k].push_back(rs); //k向自己的左右儿子连边 } void link(int k, int l, int r, int L, int R, int v) { if (L <= l && R >= r) { if (k == v) return ;//判自环 e[v].push_back(k); return ; } if (L <= mid) link(ls, l, mid, L, R, v); if (R > mid) link(rs, mid + 1, r, L, R, v); } void tarjan(int x) { dfn[x] = low[x] = ++ cnt; s.push(x), vis[x] = 1; for (int i = 0; i < e[x].size(); i ++) { int y = e[x][i]; if (!dfn[y]) { tarjan(y); low[x] = min(low[x], low[y]); } else if (vis[y]) low[x] = min(low[x], dfn[y]); } if (dfn[x] == low[x]) { idx ++; int v; do { v = s.top(); s.pop(); vis[v] = 0; bel[v] = idx; Left[idx] = min(Left[idx], a[v].l); Right[idx] = max(Right[idx], a[v].r); //更新这个强连通分量中能到的左右端点 } while (v != x); } } void dfs(int u) { vis[u] = 1; for (int i = 0; i < G[u].size(); i ++) { int v = G[u][i]; if (vis[v]) { Left[u] = min(Left[u], Left[v]); Right[u] = max(Right[u], Right[v]); continue; } dfs(v); Left[u] = min(Left[u], Left[v]); Right[u] = max(Right[u], Right[v]); } } int query(int x) { int u = bel[id[x]]; return Right[u] - Left[u] + 1; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i ++) scanf("%lld %lld", &x[i], &r[i]); memset(Left, 0x3f, sizeof Left); 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; link(1, 1, n, L, R, id[i]); a[id[i]] = {L, R}; } tarjan(1); for (int i = 1; i <= nd; i ++) { for (int j = 0; j < e[i].size(); j ++) { int u = e[i][j]; if (bel[u] == bel[i]) continue; G[bel[i]].push_back(bel[u]); } } for (int i = 1; i <= idx; i ++) {//去掉重复的边 sort(G[i].begin(), G[i].end()); unique(G[i].begin(), G[i].end()); } memset(vis, 0, sizeof vis); for (int i = 1; i <= idx; i ++) if (!vis[i]) dfs(i); for (int i = 1; i <= n; i ++) ans = (ans + 1ll * query(i) * i) % mod; printf("%d", ans); return 0; }
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】