嘟嘟嘟




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




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

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

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

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

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

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




写起来不难。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 5e5 + 5;
const int maxN = 1e6 + 5;
const int maxe = 2e7 + 5;
const ll mod = 1e9 + 7;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
} int n, du[maxN];
ll pos[maxn], rad[maxn];
struct Edge
{
int nxt, to;
}e[maxe], e2[maxe];
int head[maxN], ecnt = -1, head2[maxN], ecnt2 = -1;
In void addEdge(int x, int y)
{
e[++ecnt] = (Edge){head[x], y};
head[x] = ecnt;
}
In void addEdge2(int x, int y)
{
++du[y];
e2[++ecnt2] = (Edge){head2[x], y};
head2[x] = ecnt2;
} In ll inc(ll a, ll b) {return a + b < mod ? a + b : a + b - mod;} int tIn[maxn << 2], l[maxn << 2], r[maxn << 2], id[maxN], tcnt = 0;
In void build(int L, int R, int now)
{
l[now] = L; r[now] = R;
if(L == R) {tIn[now] = L; id[L] = now; return;}
tIn[now] = ++tcnt; id[tcnt] = now;
int mid = (L + R) >> 1;
build(L, mid, now << 1);
build(mid + 1, R, now << 1 | 1);
addEdge(tIn[now], tIn[now << 1]);
addEdge(tIn[now], tIn[now << 1 | 1]);
}
In void update(int L, int R, int now, int x)
{
if(l[now] == L && r[now] == R)
{
addEdge(x, tIn[now]);
return;
}
int mid = (l[now] + r[now]) >> 1;
if(R <= mid) update(L, R, now << 1, x);
else if(L > mid) update(L, R, now << 1 | 1, x);
else update(L, mid, now << 1, x), update(mid + 1, R, now << 1 | 1, x);
} bool in[maxN];
int st[maxN], top = 0;
int dfn[maxN], low[maxN], cnt = 0;
int col[maxN], Min[maxN], Max[maxN], ccol = 0;
In void dfs(int now)
{
dfn[now] = low[now] = ++cnt;
st[++top] = now; in[now] = 1;
for(int i = head[now], v; ~i; i = e[i].nxt)
{
if(!dfn[v = e[i].to])
{
dfs(v);
low[now] = min(low[now], low[v]);
}
else if(in[v]) low[now] = min(low[now], dfn[v]);
}
if(dfn[now] == low[now])
{
int x; ++ccol;
do
{
x = st[top--]; in[x] = 0;
col[x] = ccol;
Min[ccol] = min(Min[ccol], l[id[x]]);
Max[ccol] = max(Max[ccol], r[id[x]]);
//得另开一个数组反向记图中的点在线段树上的标号
}while(x ^ now);
}
} In void buildGraph(int now)
{
int u = col[now];
for(int i = head[now], v; ~i; i = e[i].nxt)
{
if(u == (v = col[e[i].to])) continue;
addEdge2(v, u); //建反图
}
} In void topo()
{
queue<int> q;
for(int i = 1; i <= tcnt; ++i) if(!du[i]) q.push(i);
while(!q.empty())
{
int now = q.front(); q.pop();
for(int i = head2[now], v; ~i; i = e2[i].nxt)
{
v = e2[i].to;
Min[v] = min(Min[v], Min[now]);
Max[v] = max(Max[v], Max[now]);
if(!--du[v]) q.push(v);
}
}
} int main()
{
Mem(head, -1); Mem(head2, -1);
n = read();
tcnt = n, build(1, n, 1);
for(int i = 1; i <= n; ++i) pos[i] = read(), rad[i] = read();
for(int i = 1; i <= n; ++i)
{
int L = lower_bound(pos + 1, pos + i + 1, pos[i] - rad[i]) - pos;
int R = upper_bound(pos + i + 1, pos + n + 1, pos[i] + rad[i]) - pos - 1;
update(L, R, 1, i);
}
fill(Min + 1, Min + tcnt + 1, INF);
for(int i = 1; i <= tcnt; ++i) if(!dfn[i]) dfs(i);
for(int i = 1; i <= tcnt; ++i) buildGraph(i);
topo();
ll ans = 0;
for(int i = 1; i <= n; ++i) ans = inc(ans, 1LL * i * (Max[col[i]] - Min[col[i]] + 1) % mod);
write(ans), enter;
return 0;
}

[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. 使用FakeAPP进行AI换脸必看!!

    C盘生于容量小于5G的千万别用啊!!笔者本人因为C盘只剩了3G,根本用不上这个,最后会把大小为4G的core文件必须移植到C盘当中,俺的CUDA也白安装了,而且还不小心安装成CUDA8了,应该用9好么 ...

  2. 由ODI初始化资料档案库(RUC)引起修改ORACLE字符集(ZHS16GBK-AL32UTF8)

    如果要部署代理,需要在RUC中进行资料档案库的初始化,这样可以免去配置代理的繁琐.在RUC连接数据库时会有先决条件检查,如果出现下图的警告,就需要在ORACLE中修改字符集. 具体操作如下: 登录SQ ...

  3. ASP.NET Core中使用GraphQL - 第五章 字段, 参数, 变量

    ASP.NET Core中使用GraphQL ASP.NET Core中使用GraphQL - 第一章 Hello World ASP.NET Core中使用GraphQL - 第二章 中间件 ASP ...

  4. Redis缓存你必须了解的!

    不管你是从事Python.Java.Go.PHP.Ruby等等… Redis都应该是一个比较熟悉的中间件.而大部分经常写业务代码的程序员,实际工作中或许只用到了set value.get value两 ...

  5. Redis学习笔记~分布锁的使用

    回到目录 分布锁主要用在多进程共同访问同一个资源时候,用来保持同一时间段只能有一个进程执行,同时避免了并发冲突的出现,这在很多场景都会用到,像秒杀库存,抽奖库存,多操作者处理一家公司等. void T ...

  6. Python:鲜为人知的功能特性(下)

    GitHub 上有一个名为<What the f*ck Python!>的项目,这个有趣的项目意在收集 Python 中那些难以理解和反人类直觉的例子以及鲜为人知的功能特性,并尝试讨论这些 ...

  7. Reactor 典型的 NIO 编程模型

    Doug Lea 在 Scalable IO in Java 的 PPT 中描述了 Reactor 编程模型的思想,大部分 NIO 框架和一些中间件的 NIO 编程都与它一样或是它的变体.本文结合 P ...

  8. 痞子衡嵌入式:第一本Git命令教程(0)- 索引

    大家好,我是痞子衡,是正经搞技术的痞子.本系列痞子衡给大家讲的是Git命令汇编,共12篇文章,循序渐进地介绍Git操作的完整过程. 在开始Git课程之前,需要先跟大家普及2个重要概念(四度空间.四种状 ...

  9. 浅谈Linux基本命令

    本篇文章作为Linux 入门的必备篇,主要简述Linux系统目录结构和Linux 基本Shell命令,大致内容如下: ​ 一  Linux目录及其概述 如下目录为CentOS 7目录结构 ​ 1.建立 ...

  10. 1分钟解决VS每次运行都显示“正在还原nuget程序包”问题

    VS一直停留在“正在还原nuget程序包” 在开发中,运行不同版本的vs会显示还原nuget程序包,还原需要不短的时间,并且不一定还原成功. 或者其他什么原因导致需要还原nuget程序包,这样很烦的有 ...