【SNOI2017】炸弹
题目大意
在一条直线上有\(N\)个炸弹,每个炸弹的坐标是\(X_i\),爆炸半径是 \(R_i\),
当一个炸弹爆炸时,如果另一个炸弹所在位置\(X_j\)满足:
$ X_i-R_i\leq X_j \leq X_i+R_i $
那么,该炸弹也会被引爆。
现在,请你帮忙计算一下,先把第 iii 个炸弹引爆,将引爆多少个炸弹呢?
\(0\leq N \leq 500000\),\(-10^{18} \leq X_i \leq 10^{18}\)
题目分析
考试的时候想到的是一个\(n\ log^2n\)的倍增做法。
令\(mi[i][j]\),\(mx[i][j]\)表示引爆第\(i\)个炸弹,连锁反应引爆\(2^j\)次炸弹后爆炸范围的最左端与最右端。
那么由\(2^j\)向\(2^{j+1}\)更新时,我们需要得知目前爆炸范围内其他炸弹的\(mi[i][j]\)的最小值以及\(mx[i][j]\)的最大值。这个可以用\(RMQ\)或者线段树维护。
因此总时间复杂度\(O(n\ log^2n)\),有点爆炸。
标解是线段树优化建边后,\(tarjan\)缩点+拓扑排序。
\(TLE:\)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int Maxn=500005,mod=1e9+7;
template<class T>
void read(T&x){
x=0;T f=1;char ch=getchar();
while(!isdigit(ch))ch!='-'?:f=-1,ch=getchar();
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
x*=f;
}
int n,mx[Maxn][21],mi[Maxn][21],bl[Maxn][2],br[Maxn][2],Lg[Maxn];
ll a[Maxn],r[Maxn],tmp[Maxn];
void Build(int x){
for(int i=1;i<=n;++i)mi[i][0]=bl[i][x],mx[i][0]=br[i][x];
for(int j=1;j<=18;++j)
for(int i=1;i+(1<<j)-1<=n;++i){
mi[i][j]=min(mi[i][j-1],mi[i+(1<<j-1)][j-1]);
mx[i][j]=max(mx[i][j-1],mx[i+(1<<j-1)][j-1]);
}
}
inline int ask_min(int x,int y){
int k=Lg[y-x+1];
return min(mi[x][k],mi[y-(1<<k)+1][k]);
}
inline int ask_max(int x,int y){
int k=Lg[y-x+1];
return max(mx[x][k],mx[y-(1<<k)+1][k]);
}
int main(){
read(n);
for(int i=1;i<=n;++i)read(a[i]),read(r[i]);
for(int i=1;i<=n;++i)tmp[i]=a[i],Lg[i]=log2(i);
int cur=0,pre=1;
for(int i=1;i<=n;++i){
a[i]=i;
bl[i][0]=lower_bound(tmp+1,tmp+n+1,tmp[i]-r[i])-tmp;
br[i][0]=upper_bound(tmp+1,tmp+n+1,tmp[i]+r[i])-tmp-1;
}
for(int j=1;j<=19;++j){
pre=cur;cur^=1;
Build(pre);
for(int i=1;i<=n;++i){
bl[i][cur]=ask_min(bl[i][pre],br[i][pre]);
br[i][cur]=ask_max(bl[i][pre],br[i][pre]);
}
}
ll ans=0;
for(int i=1;i<=n;++i){
ans=(ans+1ll*(br[i][cur]-bl[i][cur]+1)*i)%mod;
}
cout<<ans<<"\n";
}
\(AC:\)
#include <bits/stdc++.h>
using namespace std;
template<class T>
void read(T &x){
x=0;T f=1;char ch=getchar();
while(!isdigit(ch))ch!='-'?:f=-1,ch=getchar();
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
x*=f;
}
const int Maxn=500005,mod=1e9+7;
typedef long long ll;
int n,Pos[Maxn],h[Maxn*4];
ll a[Maxn],r[Maxn];
struct edge{int to,next;}e[Maxn*20];
void addedge(int x,int y){
static int cnt;
e[++cnt]=(edge){y,h[x]};h[x]=cnt;
}
#define ls v<<1
#define rs v<<1|1
int L[Maxn*4],R[Maxn*4],Min[Maxn*4],Max[Maxn*4];
void Build(int v,int l,int r){
L[v]=l;R[v]=r;
if(l==r)return Pos[l]=v,void();
int mid=l+r>>1;
Build(ls,l,mid);Build(rs,mid+1,r);
addedge(v,ls);addedge(v,rs);
}
void Insert(int v,int l,int r,int a,int b,int p){
if(l>b||r<a)return;
if(a<=l&&r<=b)return addedge(p,v);
int mid=l+r>>1;
Insert(ls,l,mid,a,b,p);Insert(rs,mid+1,r,a,b,p);
}
int dfn[Maxn*4],low[Maxn*4],bel[Maxn*4],st[Maxn*4],rd[Maxn*4],top,Scc,Sign;
bool in[Maxn*4];
struct info{int x,i,y;};
void dfs(int x){
static vector<info>S;
int i,y;
call:
dfn[x]=low[x]=++Sign;st[++top]=x;in[x]=1;
for(i=h[x];i;i=e[i].next){
y=e[i].to;
if(!dfn[y]){
S.push_back((info){x,i,y});
x=y;goto call;
Return:;
low[x]=min(low[x],low[y]);
}
else if(in[y])low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]){
Scc++;
Max[Scc]=0;Min[Scc]=n;
for(;;){
y=st[top--];in[y]=0;
Max[Scc]=max(Max[Scc],R[y]);
Min[Scc]=min(Min[Scc],L[y]);
bel[y]=Scc;if(y==x)break;
}
}
if(S.size()){x=S.back().x;i=S.back().i;y=S.back().y;S.pop_back();goto Return;}
}
vector<int>G[Maxn*4];
void Topsort(){
queue<int>Q;
for(int i=1;i<=Scc;i++)if(!rd[i])Q.push(i);
while(Q.size()){
int x=Q.front();Q.pop();
for(int i=0;i<G[x].size();i++){
int y=G[x][i];
if(!--rd[y])Q.push(y);
Min[y]=min(Min[y],Min[x]);
Max[y]=max(Max[y],Max[x]);
}
}
}
int main(){
read(n);
for(int i=1;i<=n;i++)read(a[i]),read(r[i]);
for(int i=1;i<=4*n;i++)L[i]=n+1,R[i]=0;
Build(1,1,n);
for(int i=1;i<=n;i++){
Insert(1,1,n,lower_bound(a+1,a+n+1,a[i]-r[i])-a,upper_bound(a+1,a+n+1,a[i]+r[i])-a-1,Pos[i]);
}
for(int i=1;i<=4*n;i++)if(!dfn[i])dfs(i);
for(int x=1;x<=4*n;x++)
for(int i=h[x];i;i=e[i].next){
int y=e[i].to;
if(bel[x]!=bel[y]){
G[bel[y]].push_back(bel[x]);
rd[bel[x]]++;
}
}
Topsort();
ll Ans=0;
for(int i=1;i<=n;i++){
Ans=(Ans+1ll*i*(Max[bel[Pos[i]]]-Min[bel[Pos[i]]]+1))%mod;
}
cout<<Ans<<"\n";
}
/*
4
1 1
5 1
6 5
15 15
*/
【SNOI2017】炸弹的更多相关文章
- [bzoj5017][Snoi2017]炸弹 tarjan缩点+线段树优化建图+拓扑
5017: [Snoi2017]炸弹 Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 608 Solved: 190[Submit][Status][ ...
- [LOJ#2255][BZOJ5017][Snoi2017]炸弹
[LOJ#2255][BZOJ5017][Snoi2017]炸弹 试题描述 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足: ...
- [SNOI2017]炸弹[线段树优化建图]
[SNOI2017]炸弹 线段树优化建图,然后跑一边tarjan把点全部缩起来,炸一次肯定是有连锁反应的所以整个连通块都一样-于是就可以发现有些是只有单向边的不能忘记更新,没了. #include & ...
- BZOJ5017题解SNOI2017炸弹--玄学递推
题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=5017 分析 老师讲课谈到了这道题,课上想出了个连边建图然后乱搞的操作,被老师钦定的递推方 ...
- [SNOI2017]炸弹
嘟嘟嘟 这题有一些别的瞎搞神奇做法,而且复杂度似乎更优,不过我为了练线段树,就乖乖的官方正解了. 做法就是线段树优化建图+强连通分量缩点+DAGdp. 如果一个炸弹\(i\)能引爆另一个炸弹\(j\) ...
- bzoj千题计划311:bzoj5017: [Snoi2017]炸弹(线段树优化tarjan构图)
https://www.lydsy.com/JudgeOnline/problem.php?id=5017 暴力: 对于每一个炸弹,枚举所有的炸弹,看它爆炸能不能引爆那个炸弹 如果能,由这个炸弹向引爆 ...
- BZOJ5017 [SNOI2017]炸弹 - 线段树优化建图+Tarjan
Solution 一个点向一个区间内的所有点连边, 可以用线段树优化建图来优化 : 前置技能传送门 然后就得到一个有向图, 一个联通块内的炸弹可以互相引爆, 所以进行缩点变成$DAG$ 然后拓扑排序. ...
- bzoj5017: [Snoi2017]炸弹
Description 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足: Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被 ...
- BZOJ5017 Snoi2017炸弹(线段树+强连通分量+缩点+传递闭包)
容易想到每个炸弹向其能引爆的炸弹连边,tarjan缩点后bitset传递闭包.进一步发现每个炸弹能直接引爆的炸弹是一段连续区间,于是线段树优化建图即可让边的数量降至O(nlogn).再冷静一下由于能间 ...
- 【bzoj5017】[Snoi2017]炸弹 线段树优化建图+Tarjan+拓扑排序
题目描述 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足: Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被引爆. 现在 ...
随机推荐
- poj3187
一.题意:给定n,求1~n的一个排列,这个排列需要满足以下两个要求:1.杨辉三角最后的和为sum 2.字典序最小 二.思路:暴力枚举每一个排列,然后计算和并与sum进行比较.这里我比较费解的是为什么 ...
- input type=checkbox的值后台怎么接受
input type=checkbox的值是on或off 实体类中这样写即可 public void setChapterVisibility(Object chapterVisibility) { ...
- Nginx + Lua搭建文件上传下载服务
收录待用,修改转载已取得腾讯云授权 最新腾讯云技术公开课直播,提问腾讯W3C代表,如何从小白成为技术专家?点击了解活动详情 作者 | 庄进发 编辑 | 迷鹿 庄进发,信息安全部后台开发工程师,主要负责 ...
- ReactJS 页面跳转保存当前scrollTop回来时,自动移动到上次浏览器的位置
在移动端的操作的时候,相信大家都遇到到这种情况,翻了好几页了,点击一项进去查,然后回来的时候,还想回来我原来的位置. google上也找了一此,有一个组件,但是好像是如果想实现这个功能,页面就得用那个 ...
- ife task0003学习笔记(四):JavaScript构造函数
JavaScript创建对象主要是3种方法:工厂模式.构造函数模式.原型模式.其实对于构造函数的概念,我们并不陌生.在之前学习c++语言的时候,也有提到过构造函数的概念.除了创建对象,构造函数(con ...
- CountDownLatch 多线程,等待所有线程结束
CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. 主要方法 public CountDownLatch(int count); 构造 ...
- 从函数作用域和块级作用域看javascript的作用域链
在ES6之前,javascript只有全局作用域和函数作用域.所谓作用域就是一个变量定义并能够被访问到的范围.也就是说如果一个变量定义在全局(window)上,那么在任何地方都能访问到这个变量,如果这 ...
- c#-foreach的秘密
foreach的秘密 class Program { static void Main(string[] args) { //创建Person的对象 Person p1=new Person(); / ...
- jquery——write less,do more
rite less, do more.这句话想必是很多语言都提倡的. 在此举三个jquery的应用体现 一.绑定多个事件类型 $("div").bind("mouseov ...
- 【学习笔记】JDBC数据库连接技术(Java Database Connectivity)
一.JDBC简介 Java是通过JDBC技术实现对各种数据库的访问的,JDBC是Java数据库连接技术的简称.它可以把数据持久保存,是一种持久化机制. 1.持久化 持久化就是将程序中的数据在瞬时状态和 ...