P5025 [SNOI2017]炸弹 题解
蒟蒻的第一篇黑题题解(学了这么长时间了才第一道也是没谁了。)
题目链接;
Solution:
朴素:
根据题目描述,我们可以处理每一个x节点左右爆炸半径范围内的点,然后模拟一次爆炸 (for),遍历每一个点。每当我们遍历到一个点,我们就对这个点在进行一次处理半径+dfs直到没有能遍历的了,直接统计遍历的点数然后输出。
时间复杂度:O(n^2)级别。
于是乎:

是不是可以暴力踩标算了呢?
显然完全接受不了。
因为这个算法要考虑到连边操作。对于每一个点,我们要将它自己连向能够遍历到的边,那么意味着,一个边要被连很多次。
就如下图:

其中编号为1的节点能炸到1 2 3 4 5,2能炸到1 2 3,3能炸到1 2 3 4 5,其中2 3 4挺惨的。
于是有了这么一个线段树建图的思路:
考虑线段树的性质,上层节点可以代表下层节点的一段区间,那么我们是不是也可以建这类的上层边,表示通过这条边能遍历到它对应的下层边的所有点。(就是这句精华)
这样我们先把所有点进行从1到n的编号,然后跑线段树的build建边,然后依次对每一个点进行从底层(只能这么理解)到上层节点的连边表示能遍历到上层节点能遍历到的节点。
这样的话,会形成很多环。在对每一个点都进行范围连边以后,对于一个环(或者强连通分量)内的点,自然能互相遍历到,为了方便统计,我们需要跑一个tarjan记录强连通分量内的点能遍历到的左右区间范围。
跑完tarjan就可以O(n)遍历一遍按照题目所说进行统计了呢。
部分代码:
build:
void build(int now,int l,int r)
{
canl[now]=l;canr[now]=r;//can表示当前节点能遍历到的左右区间范围
if(l==r)//线段树日常操作
{
id[r]=now;
return;
}
int mid=(l+r)>>;
build(now<<,l,mid);build(now<<|,mid+,r);
add(now,now<<);add(now,now<<|);//不同于线段树的地方,向左右两个节点连边,往下层伸展
}
下层往上连边部分:
void lb(int mb,int l,int r,int L,int R,int now)
{
if(l>=L&&r<=R)
{
if(mb==now)//mb表示目标节点,也就是当前初始节点
return;
add(mb,now);//now表示线段树上当前节点,从mb到now连边表示mb这个节点可以爆炸到now节点代表的区间
return;
}
int mid=(l+r)>>;
if(L<=mid)lb(mb,l,mid,L,R,now<<);//左右区间连边
if(R>mid)lb(mb,mid+,r,L,R,now<<|);
}
tarjan部分:
void tarjan(int now)
{
dfn[now]=++tarjannum;//dfn都知道是什么吧
low[now]=tarjannum;
vis[now]=;//设置入栈
st[++top]=now;
for(int i=hea[now];i;i=edge[i].nex)
{
int v=edge[i].to;
if(!dfn[v])
{
tarjan(v);low[now]=min(low[now],low[v]);
}
else
if(vis[v]) //在栈中
low[now]=min(low[now],dfn[v]);//以上正常tarjan操作
}
if(low[now]==dfn[now])
{
numdag++;//标记为同一个dag
do
{
int topp=st[top--];
dagbh[topp]=numdag;
leff[numdag]=min(leff[numdag],canl[topp]);
//在统计tarjan的同时,把这个强联通分量里面所有的点能到的左右区间范围统计一下
rigg[numdag]=max(rigg[numdag],canr[topp]);
vis[topp]=;//出栈标记
}while(st[top+]!=now);//dowhile保证能够便历到,因为上面top已经--了所以要判断top+1
}
}
高清无码完整代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define N 500003
#define modd 1000000007
using namespace std;
long long read()
{
long long ans=;
char ch=getchar(),last=' ';
while(ch<''||ch>'')last=ch,ch=getchar();
while(ch>=''&&ch<='')ans=(ans<<)+(ans<<)+ch-'',ch=getchar();
return last=='-'?-ans:ans;
}
int n,id[N],hea[N*],num,vis[N*],dagbh[N*],lef,rig,top,canl[N*],canr[N*];
int st[N*],dfn[N*],low[N*],leff[N*],rigg[N*],tarjannum,numdag;
long long r[N],x[N],ans;
struct edg{
int nex,to;
}edge[N*];
inline void add(int from,int to)
{
num++;
edge[num]={hea[from],to};
hea[from]=num;
}
void build(int now,int l,int r)
{
canl[now]=l;canr[now]=r;//can表示当前节点能遍历到的左右区间范围
if(l==r)//线段树日常操作
{
id[r]=now;
return;
}
int mid=(l+r)>>;
build(now<<,l,mid);build(now<<|,mid+,r);
add(now,now<<);add(now,now<<|);//不同于线段树的地方,向左右两个节点连边,往下层伸展 void lb(int mb,int l,int r,int L,int R,int now)
{
if(l>=L&&r<=R)
{
if(mb==now)//mb表示目标节点,也就是当前初始节点
return;
add(mb,now);//now表示线段树上当前节点
return;
}
int mid=(l+r)>>;
if(L<=mid)lb(mb,l,mid,L,R,now<<);//左右区间连边
if(R>mid)lb(mb,mid+,r,L,R,now<<|);
}
void tarjan(int now)
{
dfn[now]=++tarjannum;//dfn都知道是什么吧
low[now]=tarjannum;
vis[now]=;//设置入栈
st[++top]=now;
for(int i=hea[now];i;i=edge[i].nex)
{
int v=edge[i].to;
if(!dfn[v])
{
tarjan(v);low[now]=min(low[now],low[v]);
}
else
if(vis[v]) //在栈中
low[now]=min(low[now],dfn[v]);//以上正常tarjan操作
}
if(low[now]==dfn[now])
{
numdag++;//标记为同一个dag
do
{
int topp=st[top--];
dagbh[topp]=numdag;
leff[numdag]=min(leff[numdag],canl[topp]);
//在统计tarjan的同时,把这个强联通分量里面所有的点能到的左右区间范围统计一下
rigg[numdag]=max(rigg[numdag],canr[topp]);
vis[topp]=;//出栈标记
}while(st[top+]!=now);//dowhile保证能够便历到,因为上面top已经--了所以要判断top+1
}
}
int main()
{
memset(leff,0x3f,sizeof(leff));
n=read();
for(int i=;i<=n;i++)
{
x[i]=read(),r[i]=read();//x为横轴坐标,r为爆炸半径
}
x[n+]=0x3f3f3f3f3f3f3f3fll;
build(,,n);
for(int i=;i<=n;i++)
{
if(!r[i]) continue;
rig=upper_bound(x+,x++n,x[i]+r[i])-x-;
lef=lower_bound(x+,x++n,x[i]-r[i])-x;
lb(id[i],,n,lef,rig,);
}
tarjan();//从1号节点往下跑tarjan缩点跑成dag
for(int i=;i<=n;i++)
{
int fromm=dagbh[id[i]];
ans=(ans+((long long)i*(rigg[fromm]-leff[fromm]+))%modd)%modd;//ll
}
printf("%lld",ans%modd);
}
完结。。
可能是最水的黑题了其他的都不会做。
希望讲解过后能对泥萌有所帮助qwq
P5025 [SNOI2017]炸弹 题解的更多相关文章
- P5025 [SNOI2017]炸弹
原题链接 https://www.luogu.org/problem/P5025 闲话时刻: 第一道 AC 的黑题,虽然众人皆说水... 其实思路不是很难,代码也不是很难打,是一些我们已经学过的东西 ...
- [LOJ#2255][BZOJ5017][Snoi2017]炸弹
[LOJ#2255][BZOJ5017][Snoi2017]炸弹 试题描述 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足: ...
- [bzoj5017][Snoi2017]炸弹 tarjan缩点+线段树优化建图+拓扑
5017: [Snoi2017]炸弹 Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 608 Solved: 190[Submit][Status][ ...
- [SNOI2017]炸弹[线段树优化建图]
[SNOI2017]炸弹 线段树优化建图,然后跑一边tarjan把点全部缩起来,炸一次肯定是有连锁反应的所以整个连通块都一样-于是就可以发现有些是只有单向边的不能忘记更新,没了. #include & ...
- BZOJ5017题解SNOI2017炸弹--玄学递推
题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=5017 分析 老师讲课谈到了这道题,课上想出了个连边建图然后乱搞的操作,被老师钦定的递推方 ...
- BZOJ5017 [SNOI2017]炸弹 - 线段树优化建图+Tarjan
Solution 一个点向一个区间内的所有点连边, 可以用线段树优化建图来优化 : 前置技能传送门 然后就得到一个有向图, 一个联通块内的炸弹可以互相引爆, 所以进行缩点变成$DAG$ 然后拓扑排序. ...
- 【bzoj5017】[Snoi2017]炸弹 线段树优化建图+Tarjan+拓扑排序
题目描述 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足: Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被引爆. 现在 ...
- 洛谷P5364 [SNOI2017]礼物 题解
传送门 /* 热情好客的小猴子请森林中的朋友们吃饭,他的朋友被编号为 1∼N,每个到来的朋友都会带给他一些礼物:大香蕉.其中,第一个朋友会带给他 11 个大香蕉,之后,每一个朋友到来以后,都会带给他之 ...
- BZOJ5017 [Snoi2017]炸弹[线段树优化建边+scc缩点+DAG上DP/线性递推]
方法一: 朴素思路:果断建图,每次二分出一个区间然后要向这个区间每个点连有向边,然后一个环的话是可以互相引爆的,缩点之后就是一个DAG,求每个点出发有多少可达点. 然后注意两个问题: 上述建边显然$n ...
随机推荐
- vc++中播放声音
http://www.cnblogs.com/xuemaxiongfeng/articles/2462560.html http://bbs.csdn.net/topics/390729457 htt ...
- CentOS 7 利用qemu模拟ARM vexpress A9开发板
听说qemu用于仿真arm很不错,今日就来试了一把.由于刚刚开始,了解的并不多.本文仅仅记录Qemu装载Linux kernel和busybox根文件系统的过程.后续将会深入了解仿真的其他内容. 先上 ...
- 使用Dockerfile创建ssh服务的镜像02
使用Dockerfile创建ssh服务的镜像02 1:创建工作目录---一个镜像的所有文件都放这个目录下 ubuntu@ubuntu:~$ mkdir sshd_ubuntu ubuntu@ubunt ...
- 【持续更新】一个简洁、易用的美赛 LaTeX 模板: easyMCM
目录 1 当前美赛模板通行情况的概述 2 EasyMCM 宏包说明 2.1 与 mcmthesis 的关系之说明 2.2 easymcm宏包的简介 2.3 美赛模板下载地址 3 README 摘录 3 ...
- [LuoguP2143]巨额资金_Kruskal_Matrix-Tree定理
巨额资金 题目链接:https://www.luogu.org/problem/P2143 数据范围:略. 题解: 有一个条件是每种权值的边最多是$10$条. 但是并不知道怎么用.... 不过有一点我 ...
- base64转码java版
package com.net.util; import java.io.FileInputStream; import java.io.FileOutputStream; import java.i ...
- Prime Number(CodeForces-359C)【快速幂/思维】
题意:已知X,数组arr[n],求一个分式的分子与分母的最大公因数.分子为ΣX^arr[i],分母为X^Σarr[i],数组为不递减序列. 思路:比赛的时候以为想出了正确思路,WA掉了很多发,看了别人 ...
- Pycharm超级好用的快捷键——效率之王
Pycharm超级好用的快捷键--效率之王 IT界老黑 IT界老黑 带你领略Python的魅力 关注他 270 人赞同了该文章 最重要的快捷键 ctrl+shift+A:万能命令行 shift两次: ...
- 第9章:Python自动化管理
1.使用SSH协议访问远程服务器 SSH协议 OpenSSH协议 使用密钥登陆远程服务器 使用ssh-agent管理私钥 2.使用Polysh批量管理服务器 Polysh requires pytho ...
- Devexpress WinForm TreeList的三种数据绑定方式(DataSource绑定、AppendNode添加节点、VirtualTreeGetChildNodes(虚拟树加载模式))
第一种:DataSource绑定,这种绑定方式需要设置TreeList的ParentFieldName和KeyFieldName两个属性,这里需要注意的是KeyFieldName的值必须是唯一的. 代 ...