SNOI2017炸弹
这个东西其实我是不太会的……但是勉强卡过去了。
首先肯定是建有向图,然后求每个节点能访问的节点个数,最裸的打法就是按照题意枚举建边然后tarjan缩点,用bitset记录一下访问节点,但是bitset开不了那么大,只能拿到50%的分,至于开数组枚举我没有试,目测高不了多少。
然后思考这样一个问题,我建的那么多边真的有用么。于是只建离他最近的两个点的边,然后直接topsort统计ans,能拿到80%的数据。
然后就不会了……
去loj看一眼,有一组小的hack数据,异常难受。
看看别人的打法,不是直接统计ans,而是记录这个点所能到达的最左点l[x]和最右点r[x],然后r[x]-l[x]+1就是他能引爆的炸弹数。可以AC。
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
#include <stack>
#include <bitset>
#include <map>
#define ll long long
using namespace std;
struct EDGE {
int ed, nex;
} edge[], edgec[];
int first[], firstc[], numc, num;
struct Point {
ll x, r;
} p[];
ll read() {
ll sum = ;
int f = ;
char x = getchar();
while (x < '' || x > '') {
if (x == '-')
f = -;
x = getchar();
}
while (x >= '' && x <= '') {
sum = sum * + x - '';
x = getchar();
}
return sum * f;
}
ll n, ans1;
const int mod = 1e9 + ;
const int inf=0x7fffffff;
int dfn[], low[], sta[], bl[], du[];
int ord, sccnum, top,l[],r[];
vector<int> scc[];
bool ins[];
void add(int st, int ed) {
// cout<<"st="<<st<<" ed="<<ed<<endl;
edge[++num].ed = ed;
edge[num].nex = first[st];
first[st] = num;
}
void addc(int st, int ed) {
// cout<<"stc="<<st<<" edc="<<ed<<endl;
edgec[++numc].ed = ed;
edgec[numc].nex = firstc[st];
firstc[st] = numc;
}
void topsort() {
queue<int> q;
for (int i = ; i <= sccnum; i++)
if (!du[i])
q.push(i);
while (!q.empty()) {
int x = q.front();
q.pop();
for (int i = firstc[x]; i; i = edgec[i].nex) {
int y = edgec[i].ed;
du[y]--;
l[y]=min(l[y],l[x]);
r[y]=max(r[y],r[x]);
if (!du[y])
q.push(y);
}
}
}
void tarjan(int x) {
dfn[x] = low[x] = ++ord;
sta[++top] = x;
ins[x] = ;
for (int i = first[x]; i; i = edge[i].nex) {
int y = edge[i].ed;
if (!dfn[y]) {
tarjan(y);
low[x] = min(low[x], low[y]);
} else if (ins[y])
low[x] = min(low[x], dfn[y]);
}
if (dfn[x] == low[x]) {
sccnum++;
int p;
do {
p = sta[top--];
ins[p] = ;
l[sccnum]=min(l[sccnum],p);
r[sccnum]=max(r[sccnum],p);
bl[p] = sccnum;
scc[sccnum].push_back(p);
} while (x != p);
}
}
int main() {
n = read();
for (int i = ; i <= n; i++) {
p[i].x = read();
p[i].r = read();
}
for(int i=;i<=n;i++){
l[i]=inf;
r[i]=-inf;
}
for (int i = ; i <= n; i++){
for (int j = i + ; j <= n; j++) {
if (j == i)
continue;
if (p[j].x - p[j].r <= p[i].x ) {
add(j, i);
break;
}
}
}
for (int i = n; i >= ; i--){
for (int j = i - ; j >= ; j--) {
if (j == i)
continue;
if ( p[i].x <= p[j].x + p[j].r) {
add(j, i);
break;
}
}
}
/* for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(i==j) continue;
if(p[i].x-p[i].r<=p[j].x&&p[j].x<=p[i].x+p[i].r)
add(i,j);
}*/
for (int i = ; i <= n; i++)
if (!dfn[i])
tarjan(i);
for (int i = ; i <= n; i++) {
for (int j = first[i]; j; j = edge[j].nex) {
int y = edge[j].ed;
if (bl[i] == bl[y])
continue;
addc(bl[y], bl[i]);
du[bl[i]]++;
}
}
/* for(int i=1;i<=sccnum;i++){
for(int j=0;j<scc[i].size();j++)
cout<<scc[i][j]<<" ";
cout<<endl;
}*/
topsort();
/* for(int i=1;i<=sccnum;i++)
cout<<ans[i]<<" ";cout<<endl;*/
for (int i = ; i <= n; i++)
ans1 = (ans1 + 1ll * i * (r[bl[i]]-l[bl[i]]+)) % mod;
printf("%lld", ans1);
return ;
}
有点错位,凑合看吧,应该好打。
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,那么,该炸弹也会被引爆. 现在 ...
随机推荐
- O047、 Cinder 组件详解
参考https://www.cnblogs.com/CloudMan6/p/5585637.html cinder-api cinder-api 是整个Cinder 组件的门户,所有cinde ...
- 深入理解hive之事务处理
事务的四个特性 1.automicity:原子性 2.consistency:一致性 3. isolation:独立性 4.durability:持久性 5.支持事务有几个条件需要满足:1.所有的事务 ...
- JSR-303
JSR-303是java标准的验证框架,已有的实现由 Hibernate validator 定义的注解验证bean属性: 空检查 @Null 验证对象是否为空 @NotNull 验证对象不为空 @N ...
- powershell查看版本信息
在终端输入$PSVersionTable
- Java十进制转二进制
闲着没事写了个简单的十进制转二进制的算法,很简单,个人记录一下,不妥之处请指正. public static String toBinaryString(int j) { if (j < 0) ...
- 经典i++和++i问题(附带运算符优先级问题)
转自 https://blog.csdn.net/mustard1020/article/details/79617865 1.i++和++i的区别 (1)i++简单来说就是先用i的值来参加表 ...
- C#微信公众平台账号开发真正给初学者的文章
微信越来越受到大众人群的喜爱,但是对于开发人员来说刚接触肯能还是一头雾水的,比如像我,看了三四天文档感觉要吐,但是程序还是要写知识还是要学.发现了一个比较适合初学者的文章送给大家,废话到此:(转贴吧) ...
- 【CF335 E】Counting Skyscrapers
题意 有一排高楼,每一栋高楼有一个正整数高度,高度为 \(i\) 的概率为 \(2^{-i}\).一栋楼的每层从下往上依次编号为 \(0,1,2,\cdots,i-1\). 为了出题,大楼之间安装了溜 ...
- 需要以管理员的身份运行程序(winform)
1.添加应用程序清单文件(app.manifest) 2.打开app.manifest,将<requestedExecutionLevel level="asInvoker" ...
- Python模块struct(二进制数据服务)
struct模块 Python没有专门处理字节的数据类型.但由于b'str'可以表示字节,所以,字节数组=二进制str. 而在C语言中,我们可以很方便地用struct.union来处理字节,以及字节和 ...