嘟嘟嘟




这题有一些别的瞎搞神奇做法,而且复杂度似乎更优,不过我为了练线段树,就乖乖的官方正解了。




做法就是线段树优化建图+强连通分量缩点+DAGdp。

如果一个炸弹\(i\)能引爆另一个炸弹\(j\),就从\(i\)向\(j\)连边。然后我们从图上每一个点dfs,能走到的点就是他最终能引爆的炸弹数量。

但这个复杂度显然不行。首先连边太多。然后显而易见的是一个炸弹能引爆的炸弹范围是一个连续区间,所以用线段树优化一下就好了。

然后每一个点dfs显然也太捞。所以我们先缩点,然后在DAG上反向dp就很棒棒了。

缩点和dp的时候维护每一个点能向左和向右到达最远的区间,这样每一个点能引爆的炸弹数量就是区间长度了。

刚开始我想直接维护炸弹数量。但第一个困难是有一些点是线段树上的虚点(非叶子节点),不应该被算上,然后我就想给这个点附上0的权值,到时候加权值就好了。第二个困难是dp的时候两个点的引爆范围可能重叠,那么单纯的数量相加就必定会gg了。




写起来不难。

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<cmath>
  4. #include<algorithm>
  5. #include<cstring>
  6. #include<cstdlib>
  7. #include<cctype>
  8. #include<vector>
  9. #include<stack>
  10. #include<queue>
  11. using namespace std;
  12. #define enter puts("")
  13. #define space putchar(' ')
  14. #define Mem(a, x) memset(a, x, sizeof(a))
  15. #define In inline
  16. typedef long long ll;
  17. typedef double db;
  18. const int INF = 0x3f3f3f3f;
  19. const db eps = 1e-8;
  20. const int maxn = 5e5 + 5;
  21. const int maxN = 1e6 + 5;
  22. const int maxe = 2e7 + 5;
  23. const ll mod = 1e9 + 7;
  24. inline ll read()
  25. {
  26. ll ans = 0;
  27. char ch = getchar(), last = ' ';
  28. while(!isdigit(ch)) last = ch, ch = getchar();
  29. while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
  30. if(last == '-') ans = -ans;
  31. return ans;
  32. }
  33. inline void write(ll x)
  34. {
  35. if(x < 0) x = -x, putchar('-');
  36. if(x >= 10) write(x / 10);
  37. putchar(x % 10 + '0');
  38. }
  39. int n, du[maxN];
  40. ll pos[maxn], rad[maxn];
  41. struct Edge
  42. {
  43. int nxt, to;
  44. }e[maxe], e2[maxe];
  45. int head[maxN], ecnt = -1, head2[maxN], ecnt2 = -1;
  46. In void addEdge(int x, int y)
  47. {
  48. e[++ecnt] = (Edge){head[x], y};
  49. head[x] = ecnt;
  50. }
  51. In void addEdge2(int x, int y)
  52. {
  53. ++du[y];
  54. e2[++ecnt2] = (Edge){head2[x], y};
  55. head2[x] = ecnt2;
  56. }
  57. In ll inc(ll a, ll b) {return a + b < mod ? a + b : a + b - mod;}
  58. int tIn[maxn << 2], l[maxn << 2], r[maxn << 2], id[maxN], tcnt = 0;
  59. In void build(int L, int R, int now)
  60. {
  61. l[now] = L; r[now] = R;
  62. if(L == R) {tIn[now] = L; id[L] = now; return;}
  63. tIn[now] = ++tcnt; id[tcnt] = now;
  64. int mid = (L + R) >> 1;
  65. build(L, mid, now << 1);
  66. build(mid + 1, R, now << 1 | 1);
  67. addEdge(tIn[now], tIn[now << 1]);
  68. addEdge(tIn[now], tIn[now << 1 | 1]);
  69. }
  70. In void update(int L, int R, int now, int x)
  71. {
  72. if(l[now] == L && r[now] == R)
  73. {
  74. addEdge(x, tIn[now]);
  75. return;
  76. }
  77. int mid = (l[now] + r[now]) >> 1;
  78. if(R <= mid) update(L, R, now << 1, x);
  79. else if(L > mid) update(L, R, now << 1 | 1, x);
  80. else update(L, mid, now << 1, x), update(mid + 1, R, now << 1 | 1, x);
  81. }
  82. bool in[maxN];
  83. int st[maxN], top = 0;
  84. int dfn[maxN], low[maxN], cnt = 0;
  85. int col[maxN], Min[maxN], Max[maxN], ccol = 0;
  86. In void dfs(int now)
  87. {
  88. dfn[now] = low[now] = ++cnt;
  89. st[++top] = now; in[now] = 1;
  90. for(int i = head[now], v; ~i; i = e[i].nxt)
  91. {
  92. if(!dfn[v = e[i].to])
  93. {
  94. dfs(v);
  95. low[now] = min(low[now], low[v]);
  96. }
  97. else if(in[v]) low[now] = min(low[now], dfn[v]);
  98. }
  99. if(dfn[now] == low[now])
  100. {
  101. int x; ++ccol;
  102. do
  103. {
  104. x = st[top--]; in[x] = 0;
  105. col[x] = ccol;
  106. Min[ccol] = min(Min[ccol], l[id[x]]);
  107. Max[ccol] = max(Max[ccol], r[id[x]]);
  108. //得另开一个数组反向记图中的点在线段树上的标号
  109. }while(x ^ now);
  110. }
  111. }
  112. In void buildGraph(int now)
  113. {
  114. int u = col[now];
  115. for(int i = head[now], v; ~i; i = e[i].nxt)
  116. {
  117. if(u == (v = col[e[i].to])) continue;
  118. addEdge2(v, u); //建反图
  119. }
  120. }
  121. In void topo()
  122. {
  123. queue<int> q;
  124. for(int i = 1; i <= tcnt; ++i) if(!du[i]) q.push(i);
  125. while(!q.empty())
  126. {
  127. int now = q.front(); q.pop();
  128. for(int i = head2[now], v; ~i; i = e2[i].nxt)
  129. {
  130. v = e2[i].to;
  131. Min[v] = min(Min[v], Min[now]);
  132. Max[v] = max(Max[v], Max[now]);
  133. if(!--du[v]) q.push(v);
  134. }
  135. }
  136. }
  137. int main()
  138. {
  139. Mem(head, -1); Mem(head2, -1);
  140. n = read();
  141. tcnt = n, build(1, n, 1);
  142. for(int i = 1; i <= n; ++i) pos[i] = read(), rad[i] = read();
  143. for(int i = 1; i <= n; ++i)
  144. {
  145. int L = lower_bound(pos + 1, pos + i + 1, pos[i] - rad[i]) - pos;
  146. int R = upper_bound(pos + i + 1, pos + n + 1, pos[i] + rad[i]) - pos - 1;
  147. update(L, R, 1, i);
  148. }
  149. fill(Min + 1, Min + tcnt + 1, INF);
  150. for(int i = 1; i <= tcnt; ++i) if(!dfn[i]) dfs(i);
  151. for(int i = 1; i <= tcnt; ++i) buildGraph(i);
  152. topo();
  153. ll ans = 0;
  154. for(int i = 1; i <= n; ++i) ans = inc(ans, 1LL * i * (Max[col[i]] - Min[col[i]] + 1) % mod);
  155. write(ans), enter;
  156. return 0;
  157. }

[SNOI2017]炸弹的更多相关文章

  1. [bzoj5017][Snoi2017]炸弹 tarjan缩点+线段树优化建图+拓扑

    5017: [Snoi2017]炸弹 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 608  Solved: 190[Submit][Status][ ...

  2. [LOJ#2255][BZOJ5017][Snoi2017]炸弹

    [LOJ#2255][BZOJ5017][Snoi2017]炸弹 试题描述 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足: ...

  3. [SNOI2017]炸弹[线段树优化建图]

    [SNOI2017]炸弹 线段树优化建图,然后跑一边tarjan把点全部缩起来,炸一次肯定是有连锁反应的所以整个连通块都一样-于是就可以发现有些是只有单向边的不能忘记更新,没了. #include & ...

  4. BZOJ5017题解SNOI2017炸弹--玄学递推

    题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=5017 分析 老师讲课谈到了这道题,课上想出了个连边建图然后乱搞的操作,被老师钦定的递推方 ...

  5. bzoj千题计划311:bzoj5017: [Snoi2017]炸弹(线段树优化tarjan构图)

    https://www.lydsy.com/JudgeOnline/problem.php?id=5017 暴力: 对于每一个炸弹,枚举所有的炸弹,看它爆炸能不能引爆那个炸弹 如果能,由这个炸弹向引爆 ...

  6. BZOJ5017 [SNOI2017]炸弹 - 线段树优化建图+Tarjan

    Solution 一个点向一个区间内的所有点连边, 可以用线段树优化建图来优化 : 前置技能传送门 然后就得到一个有向图, 一个联通块内的炸弹可以互相引爆, 所以进行缩点变成$DAG$ 然后拓扑排序. ...

  7. bzoj5017: [Snoi2017]炸弹

    Description 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足:  Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被 ...

  8. BZOJ5017 Snoi2017炸弹(线段树+强连通分量+缩点+传递闭包)

    容易想到每个炸弹向其能引爆的炸弹连边,tarjan缩点后bitset传递闭包.进一步发现每个炸弹能直接引爆的炸弹是一段连续区间,于是线段树优化建图即可让边的数量降至O(nlogn).再冷静一下由于能间 ...

  9. 【bzoj5017】[Snoi2017]炸弹 线段树优化建图+Tarjan+拓扑排序

    题目描述 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足:  Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被引爆.  现在 ...

随机推荐

  1. asp.net core 2.0的认证和授权

    在asp.net core中,微软提供了基于认证(Authentication)和授权(Authorization)的方式,来实现权限管理的,本篇博文,介绍基于固定角色的权限管理和自定义角色权限管理, ...

  2. 【安富莱STM32H7教程】第1章 初学STM32H7的准备工作

    完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第1章   初学STM32H7的准备工作 俗话说万事开头 ...

  3. android学习笔记--检测是否为wifi环境

    ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); // 获取 ...

  4. Python基础(zip方法)

    zip函数: 描述:将zip函数中的两个可迭代对象参数按对应索引值进行匹配组合,得到zip对象.(拉链式函数) zip函数简单应用如下: #-----------------zip函数-------- ...

  5. Linux知识要点(文件压缩打包解压缩)

    tar 的选项与参数非常的多!我们只讲几个常用的选项,更多选项您可以自行 man tar 查询啰! 其实最简单的使用 tar 就只要记忆底下的方式即可(gzip方式): 压 缩: tar -zcvf ...

  6. Docker 查看镜像信息

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 文章首发个人网站: https://ww ...

  7. Linux配置成网关

    之前按照诸葛建伟的metasploit魔鬼训练营搭了个简单的内网环境,其中就是一台linux作为内网网关.但是虚拟机开启的时候经常出现包不能转发的情况,也就是网关没有配置好. 在网上找了好几个才找到适 ...

  8. 图像检索(5):基于OpenCV实现小型的图像数据库检索

    本文对前面的几篇文章进行个总结,实现一个小型的图像检索应用. 一个小型的图像检索应用可以分为两部分: train,构建图像集的特征数据库. retrieval,检索,给定图像,从图像库中返回最类似的图 ...

  9. jquery获取元素(父级的兄弟元素的子元素)

    一.获取父级元素 使用jquery获取父级元素: parent() 例如:$(this).parent('ul'); 二.获取同级元素 使用jquery获取同级元素:siblings() 例如:$(t ...

  10. Springboot 系列(九)使用 Spring JDBC 和 Druid 数据源监控

    前言 作为一名 Java 开发者,相信对 JDBC(Java Data Base Connectivity)是不会陌生的,JDBC作为 Java 基础内容,它提供了一种基准,据此可以构建更高级的工具和 ...