bzoj 5017 [Snoi2017]炸弹
题面
https://www.lydsy.com/JudgeOnline/problem.php?id=5017
题解
如果数据范围小一点那么就缩点 然后跑一个基础的DAG上的dp就好了
但是边数是$O(n^2)$的 所以就会炸
然后发现题目的特殊性
每一个点连向的点是连续的 换言之就是每个点和一个连续的段连上了一条边
那么我们把段拆开 用类似线段树的做法拆成log个小段 然后把这个点与这些小段连边即可
记得把每个段向它的儿子连上边
这样点数和边数都是$O(n \log n)$ 就可以了
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll; ll read(){
ll x=,f=;char c=getchar();
while(c<'' || c>''){if(c=='-')f=-;c=getchar();}
while(c>='' && c<=''){x=x*+c-'';c=getchar();}
return x*f;
} const int mod=1e9+;
const int maxn=;
int n,nwn;
ll pos[maxn],rad[maxn];
struct Node{
int l,r;
} tr[maxn*]; int dy[maxn]; inline bool pd(int a,int b){
return abs(pos[a]-pos[b])<=rad[a];
} int head[maxn*],nxt[maxn*],to[maxn*],cnt;
int rhead[maxn*],rnxt[maxn*],rto[maxn*],rcnt; inline void add_edge(int a,int b){
to[++cnt]=b;
nxt[cnt]=head[a];
head[a]=cnt;
rto[++rcnt]=a;
rnxt[rcnt]=rhead[b];
rhead[b]=rcnt;
} void build(int i,int l,int r){
tr[i].l=l,tr[i].r=r;
nwn=max(nwn,i);
if(l==r){
dy[l]=i;return;
}
int md=(l+r)>>;
build(i*,l,md);
build(i*+,md+,r);
add_edge(i,i*);
add_edge(i,i*+);
} inline void work(int ind,int i,int l,int r){
if(tr[i].l>r || tr[i].r<l) return;
if(tr[i].l>=l && tr[i].r<=r){
add_edge(dy[ind],i);
return;
}
work(ind,i*,l,r);
work(ind,i*+,l,r);
} int vis[maxn*];
int seq[maxn*],tot;
int col; void dfs1(int nw){
vis[nw]=;
for(int i=rhead[nw];i;i=rnxt[i]){
int v=rto[i];
if(vis[v]) continue;
dfs1(v);
}
seq[++tot]=nw;
} void dfs2(int nw){
vis[nw]=col;
for(int i=head[nw];i;i=nxt[i]){
int v=to[i];
if(vis[v]) continue;
dfs2(v);
}
} int sz[maxn*];
int ans[maxn*];
bool vis2[maxn*]; void dfs3(int nw){
if(ans[vis[nw]]==) ans[vis[nw]]=sz[vis[nw]];
vis2[nw]=;
for(int i=head[nw];i;i=nxt[i]){
int v=to[i];
if(vis2[v]) continue;
dfs3(v);
if(vis[v]!=vis[nw])
ans[vis[nw]]+=ans[vis[v]];
}
} int main(){
#ifdef LZT
freopen("in","r",stdin);
#endif
n=read();
build(,,n);
for(int i=;i<=n;i++)
pos[i]=read(),rad[i]=read();
int l,r;
for(int i=;i<=n;i++){
int L=,R=i;
while(L<=R){
int md=(L+R)>>;
if(pos[i]-pos[md]>rad[i]) L=md+;
else R=md-,l=md;
}
L=i,R=n;
while(L<=R){
int md=(L+R)>>;
if(pos[md]-pos[i]>rad[i]) R=md-;
else L=md+,r=md;
}
work(i,,l,r);
} for(int i=;i<=nwn;i++){
if(tr[i].l==) continue;
if(!vis[i]) dfs1(i);
}
memset(vis,,sizeof(vis)); for(int i=tot;i>=;i--)
if(!vis[seq[i]]) ++col,dfs2(seq[i]); for(int i=;i<=nwn;i++) if(tr[i].l && tr[i].l==tr[i].r) sz[vis[i]]++;
dfs3(); ll res=;
for(int i=;i<=nwn;i++){
if(tr[i].l<tr[i].r) continue;
res+=tr[i].l*1ll*ans[vis[i]]%mod;
res%=mod;
}
printf("%lld\n",res); return ;
} /*
4
1 1
5 1
6 5
15 15
*/
Review
动机?
首先缩点+dp是显然的
然后我就没有想到怎么优化
其实也不难想啊 就是要把连续的一段点转化成几个点 也就是区间->几个点 那么就上线段树嘛
然后写的时候注意总点数的处理
按照我的线段树写法 一棵树中 最小编号到最大编号之间不连续 也就是有一些编号不对应任何点
这样在遍历所有点的时候要判断一下当前编号是否存在
bzoj 5017 [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 & ...
- bzoj千题计划311:bzoj5017: [Snoi2017]炸弹(线段树优化tarjan构图)
https://www.lydsy.com/JudgeOnline/problem.php?id=5017 暴力: 对于每一个炸弹,枚举所有的炸弹,看它爆炸能不能引爆那个炸弹 如果能,由这个炸弹向引爆 ...
- bzoj 5017 炸弹
题目大意: 直线上有n个炸弹有坐标x和半径r 当一个炸弹被引爆时 若有炸弹的坐标在该炸弹坐标+-r范围内则另一个炸弹也被引爆 求先引爆每一个炸弹最终会引爆多少炸弹 思路: 可以想到n平方连边然后tar ...
- BZOJ5017题解SNOI2017炸弹--玄学递推
题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=5017 分析 老师讲课谈到了这道题,课上想出了个连边建图然后乱搞的操作,被老师钦定的递推方 ...
- bzoj5017 [Snoi2017]炸弹 (线段树优化建图+)tarjan 缩点+拓扑排序
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=5017 题解 这个题目方法挺多的. 线段树优化建图 线段树优化建图的做法应该挺显然的,一个炸弹能 ...
- [SNOI2017]炸弹
嘟嘟嘟 这题有一些别的瞎搞神奇做法,而且复杂度似乎更优,不过我为了练线段树,就乖乖的官方正解了. 做法就是线段树优化建图+强连通分量缩点+DAGdp. 如果一个炸弹\(i\)能引爆另一个炸弹\(j\) ...
- BZOJ5017 [SNOI2017]炸弹 - 线段树优化建图+Tarjan
Solution 一个点向一个区间内的所有点连边, 可以用线段树优化建图来优化 : 前置技能传送门 然后就得到一个有向图, 一个联通块内的炸弹可以互相引爆, 所以进行缩点变成$DAG$ 然后拓扑排序. ...
随机推荐
- 【献给CWNU的师弟】Web篇
2014年10月8日 献给CWNU的师弟
- 自己定义msi安装包的运行过程
有时候我们须要在程序中运行还有一个程序的安装.这就须要我们去自己定义msi安装包的运行过程. 比方我要做一个安装管理程序,能够依据用户的选择安装不同的子产品.当用户选择了三个产品时,假设分别显示这三个 ...
- 网页 H5“线条” 特效实现方式(canvas-nest)
先上图 (看博客空白处也可以呦): 前一阵浏览网站的时候,发现了这个好玩的东西,一直想找找怎么实现的,今天忙里偷闲,上网搜了一下,发现实现起来特别简单. 只需要在网页body里引入一个<scri ...
- 使用Qt发送HTTPS请求
示例代码: #include "mainwindow.h" #include "ui_mainwindow.h" #include <QNetworkAc ...
- js中!~什么意思
(function () { var names = []; return function (name) { addName(name); } function addName(name) { if ...
- 对于iOS 7 隐藏特性和解决之道
当 iOS7 刚发布的时候,全世界的苹果开发人员都立马尝试着去编译他们的app,接着再花上数月的时间来修复任何出现的故障,甚至重做app.这样的结果,使得人们根本无暇去探究 iOS7 所带来的新东西. ...
- js 中call,apply,bind的区别
call.apply.bind方法的共同点与区别: apply.call.bind 三者都是用来改变函数的this对象的指向: apply.call.bind 三者都可以利用后续参数传参: bind ...
- ap和路由器有什么区别 ap和路由器的区别介绍【图文】
现在能够摆脱网线限制能够自由方便上网的WiFi和无线网络也来越流行,很多酒店.饭店.宾馆.办公楼等地方都会提供无线网络.而能够提供无线网络的设备有很多,现在我们介绍的是无线ap和无线路由器.那么,ap ...
- MYSQL进阶学习笔记二:MySQL存储过程和局部变量!(视频序号:进阶_4-6)
知识点三:MySQL存储过程和局部变量(4,5,6) 存储过程的创建: 创建存储过程的步骤: 首先选中数据库 改变分隔符,不让分号作为执行结束的标记.(通常情况下,改变分隔符命令 DELIMI ...
- hdu 3336 Count the string(next数组)
题意:统计前缀在串中出现的次数 思路:next数组,递推 #include<iostream> #include<stdio.h> #include<string.h&g ...