题面

传送门

分析

主体思路:若x能引爆y,从x向y连一条有向边,最后的答案就是从x出发能够到达的点的个数

首先我们发现一个炸弹可以波及到的范围一定是坐标轴上的一段连续区间

我们可以用二分查找求出炸弹能波及到最左边和最右边的点,记为[l,r]

然后我们就需要向编号属于区间[l,r]的点连一条有向边

如果直接连边,时间复杂度为\(O(n^2)\) 无法接受,考虑用线段树优化连边

我们将线段树看成一个有向图,每个线段树节点看成图上的一个点,[l,r]向[l,mid],[mid+1,r]连边,叶子节点[l,l]向原图上的节点l连边

对于从x向编号属于区间[L,R]的点连边,我们用类似线段树区间更新的方法,将[L,R]拆成许多个小区间,再直接向这些小区间暴力连边



根据线段树的性质,最多会分出\(\left[ \log _{2}n\right]\)个节点,所以单次连边的时间复杂度为\(O(\log n)\)

然后就很套路了,显然环上的点可以缩成一个大点(权值为环上所有节点权值之和(线段树节点权值为0,原图上节点权值为1))

Tarjan完在DAG上DP即可

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<stack>
#include<queue>
#include<vector>
#define maxn 1700005
#define mod 1000000007
using namespace std;
inline void qread(int &x) {
x=0;
int sign=1;
char c=getchar();
while(c<'0'||c>'9') {
if(c=='-') sign=-1;
c=getchar();
}
while(c>='0'&&c<='9') {
x=x*10+c-'0';
c=getchar();
}
x=x*sign;
}
inline void qread(long long &x) {
x=0;
long long sign=1;
char c=getchar();
while(c<'0'||c>'9') {
if(c=='-') sign=-1;
c=getchar();
}
while(c>='0'&&c<='9') {
x=x*10+c-'0';
c=getchar();
}
x=x*sign;
} int n;
long long x[maxn];
long long r[maxn]; struct edge {
int from;
int to;
edge() { }
edge(int u,int v) {
from=u;
to=v;
}
friend bool operator == (edge a,edge b) {
return a.to==b.to&&a.from==b.from;
}
friend bool operator < (edge a,edge b) {
if(a.from==b.from) return a.to<b.to;
else return a.from<b.from;
}
};
set<edge>vis1;
set<edge>vis2;
vector<int>G[maxn],D[maxn];
int w[maxn];
void add_edge(int u,int v) {
G[u].push_back(v);
} int newn=n;
struct node {
int l;
int r;
} tree[maxn];
void build(int l,int r,int pos) {
newn++;
tree[pos].l=l;
tree[pos].r=r;
if(l==r) {
add_edge(pos+n,l);
return;
}
add_edge(pos+n,pos*2+n);
add_edge(pos+n,pos*2+1+n);
int mid=(l+r)>>1;
build(l,mid,pos<<1);
build(mid+1,r,pos<<1|1);
}
void update(int L,int R,int v,int pos) {
if(L<=tree[pos].l&&R>=tree[pos].r) {
add_edge(v,pos+n);
return;
}
int mid=(tree[pos].l+tree[pos].r)>>1;
if(L<=mid) update(L,R,v,pos<<1);
if(R>mid) update(L,R,v,pos<<1|1);
} stack<int>s;
int tim=0;
int m=0;
int ins[maxn];
int dfn[maxn];
int low[maxn];
int belong[maxn];
int sz[maxn];
void tarjan(int x) {
s.push(x);
ins[x]=1;
dfn[x]=low[x]=++tim;
int tmp=G[x].size();
for(int i=0; i<tmp; i++) {
int y=G[x][i];
if(!dfn[y]) {
tarjan(y);
low[x]=min(low[x],low[y]);
} else if(ins[y]) {
low[x]=min(low[x],dfn[y]);
}
}
if(low[x]==dfn[x]) {
m++;
int y;
do {
y=s.top();
s.pop();
ins[y]=0;
belong[y]=m;
sz[m]+=w[y];
} while(x!=y);
}
} void dcg_to_dag() {
for(int i=1; i<=n; i++) {
if(!dfn[i]) tarjan(i);
}
int s;
for(int i=1; i<=n; i++) {
s=G[i].size();
for(int j=0; j<s; j++) {
if(belong[i]!=belong[G[i][j]]&&!vis2.count(edge(belong[i],belong[G[i][j]]))) {
vis2.insert(edge(belong[i],belong[G[i][j]]));
D[belong[i]].push_back(belong[G[i][j]]);
}
}
}
} long long dp[maxn];
int dfs(int x){
if(dp[x]) return dp[x];
dp[x]=sz[x];
int tmp=D[x].size();
for(int i=0;i<tmp;i++){
int y=D[x][i];
dp[x]+=dfs(y);
}
return dp[x];
} long long solve() {
long long ans=0;
for(int i=1;i<=n;i++){
if(!dp[i]) dfs(i);
}
for(int i=1; i<=n; i++) {
ans=(ans+w[i]*i*dp[belong[i]]%mod)%mod;
}
return ans;
} int main() {
int L,R;
qread(n);
for(int i=1; i<=n; i++) {
w[i]=1;
qread(x[i]);
qread(r[i]);
}
newn=n;
build(1,n,1);
for(int i=1; i<=n; i++) {
L=lower_bound(x+1,x+1+n,x[i]-r[i])-x;
R=upper_bound(x+1,x+1+n,x[i]+r[i])-x-1;
update(L,R,i,1);
}
n=newn;
dcg_to_dag();
printf("%lld\n",solve());
}

Libre OJ 2255 (线段树优化建图+Tarjan缩点+DP)的更多相关文章

  1. 【2019.7.26 NOIP模拟赛 T3】化学反应(reaction)(线段树优化建图+Tarjan缩点+拓扑排序)

    题意转化 考虑我们对于每一对激活关系建一条有向边,则对于每一个点,其答案就是其所能到达的点数. 于是,这个问题就被我们搬到了图上,成了一个图论题. 优化建图 考虑我们每次需要将一个区间向一个区间连边. ...

  2. bzoj5017 [Snoi2017]炸弹 (线段树优化建图+)tarjan 缩点+拓扑排序

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=5017 题解 这个题目方法挺多的. 线段树优化建图 线段树优化建图的做法应该挺显然的,一个炸弹能 ...

  3. bzoj5017 炸弹 (线段树优化建图+tarjan+拓扑序dp)

    直接建图边数太多,用线段树优化一下 然后缩点,记下来每个点里有多少个炸弹 然后按拓扑序反向dp一下就行了 #include<bits/stdc++.h> #define pa pair&l ...

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

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

  5. 炸弹:线段树优化建边+tarjan缩点+建反边+跑拓扑

    这道题我做了有半个月了...终于A了... 有图为证 一句话题解:二分LR线段树优化建边+tarjan缩点+建反边+跑拓扑统计答案 首先我们根据题意,判断出来要炸弹可以连着炸,就是这个炸弹能炸到的可以 ...

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

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

  7. 『炸弹 线段树优化建图 Tarjan』

    炸弹(SNOI2017) Description 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸 时,如果另一个炸弹所在位置 Xj 满足: Xi−Ri≤Xj≤Xi ...

  8. 【2019北京集训2】duck 线段树优化建图+tarjan

    题目大意:给你$n$个点,第$i$个点有点权$v_i$.你需要将这$n$个点排成一排,第$i$个点的点权能被累加当且仅当这个点前面存在编号在$[l_i,r_i]$中的点,问你这些点应该如何排列,点权和 ...

  9. BZOJ5017 炸弹(线段树优化建图+Tarjan+拓扑)

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

随机推荐

  1. v-cloak解决Vue双大括号闪烁问题

    相信不少人和我一样,初次查看一个技术的文档的时候,知识吸收的很慢,因为对这个技术的不熟悉导致不清楚各种操作的应用场景,当我意识到这件事之后,我决定换种学习思路,即以实战为主,卡壳就查文档,会对这个技术 ...

  2. 编辑器和IDE的区别以及WebStorm和PhpStorm的区别

    编辑器和IDE的区别: 编辑器就是纯粹编辑文本的编辑器,识别级别在文字级,只显示你想写入和打开的文本内容,不管你写什么内容,只提供接收与显示功能,Windows自带的写字板就是最简单的编辑器.举个生活 ...

  3. [转载]浅谈JavaScript函数重载

     原文地址:浅谈JavaScript函数重载 作者:ChessZhang 上个星期四下午,接到了网易的视频面试(前端实习生第二轮技术面试).面了一个多小时,自我感觉面试得很糟糕的,因为问到的很多问题都 ...

  4. Windows 10 系统获取密钥方法

    方法一: 快捷键 win+R 打开运行窗口,输入 regedit 打开注册表编辑器,选择 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\Curren ...

  5. qt05 音乐播放器

    这些步骤可实现音乐播放,但是列表不能显示 music = new QMediaPlayer(this); playlist = new QMediaPlaylist(this); playlist-& ...

  6. java程序员必知的 8大排序

    Java常用的八种排序算法与代码实现 排序问题一直是程序员工作与面试的重点,今天特意整理研究下与大家共勉!这里列出8种常见的经典排序,基本涵盖了所有的排序算法. 1.直接插入排序 我们经常会到这样一类 ...

  7. 设置div 居中 和得到dom元素高度

    javascript中获取dom元素高度和宽度的方法如下: 网页可见区域宽: document.body.clientWidth网页可见区域高: document.body.clientHeight网 ...

  8. js如何判断用户使用的设备类型及平台

    前端开发经常遇到需要判断用户的浏览设备,是pc端还是移动端,移动端使用的是什么手机系统?android.ios.ipad.windows phone等等,有时候还需要知道用户浏览页面是在微信中打开还是 ...

  9. 转载-使用Nodepad++来编辑我们服务器的配置文件

    转自------------------ 作者:李阿昀 来源:CSDN 原文:https://blog.csdn.net/yerenyuan_pku/article/details/73128819 ...

  10. iOS设计模式之适配器模式

    一,适配器的定义 定义 将一个类的接口转换成客户希望的另外一个接口.适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作 需求场景 需要使用以前开发的“一些现存的对象”,但是新环境中要求 ...