卧薪尝胆,厚积薄发。
SNOI2017 炸弹
Date: Sun Jul 15 18:18:18 CST 2018 In Category: NoCategory

Description:

在一条直线上有 $N$ 个炸弹,每个炸弹的坐标是 $X_i$ ,爆炸半径是 $R_i$ ,当一个炸弹爆炸时,如果另一个炸弹所在位置 $X_j$ 满足: $X_i−R_i\le X_j\le X_i+R_i$ ,那么,该炸弹也会被引爆。
求 $$\begin{align}\sum_{i=1}^n{i\times炸弹i能引爆的炸弹个数}\end{align}$ $

Solution:

当一个炸弹 $i$ 被引爆时,对于能够被当前这颗炸弹 $i$ 引爆的炸弹 $j$ ,建一条 $i\to j$ 的边,但是由于题目中的边数较多,所以不可能这样建图。当一个炸弹被引爆,那么最总一共被引爆的炸弹一定是连续的,那么区间问题我们就可以用线段树来做。
建完边后, $tarjan$ 缩点,最后 $toposort$ ,反向建图,从最远的点一步一步的推回去,记录能炸掉的边界 $lbound$ 和 $rbound$ ,即可得到每个能炸掉的个数。
模数一定不要 $+=$ !!!

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<stack>
#include<map>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
int n;
#define MAXN 500010
#define MOD 1000000007ll
typedef long long ll;
ll x[MAXN],r[MAXN];
ll to[MAXN],tot;
map<ll,int> p;
struct edge
{
int to,nxt;
}e[MAXN << 5];
int lin[MAXN << 1] = {0};
int edgenum = 0;
void add(int a,int b)
{
++edgenum;e[edgenum].to = b;e[edgenum].nxt = lin[a];lin[a] = edgenum;
return;
}
struct node
{
int lc,rc;
}t[MAXN << 1];
int ptr = 0;
int newnode(){return ++ptr;}
int root;
#define mid ((l + r) >> 1)
int lef[MAXN];
void build(int &rt,int l,int r)
{
rt = newnode();
if(l == r)
{
lef[l] = rt;
return;
}
build(t[rt].lc,l,mid);add(rt,t[rt].lc);
build(t[rt].rc,mid + 1,r);add(rt,t[rt].rc);
return;
}
void set(int rt,int k,int L,int R,int l,int r)
{
if(L <= l && r <= R)
{
add(k,rt);
return;
}
if(L <= mid)set(t[rt].lc,k,L,R,l,mid);
if(R > mid)set(t[rt].rc,k,L,R,mid + 1,r);
return;
}
int dfn[MAXN << 1],low[MAXN << 1],tag = 0,ins[MAXN << 1],scc = 0;
bool v[MAXN << 1];
stack<int> s;
ll lbound[MAXN << 1],rbound[MAXN << 1];
void tarjan(int k)
{
dfn[k] = low[k] = ++tag;
v[k] = true;s.push(k);
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(dfn[e[i].to] == 0)
{
tarjan(e[i].to);
low[k] = min(low[k],low[e[i].to]);
}
else if(v[e[i].to])
{
low[k] = min(low[k],dfn[e[i].to]);
}
}
if(low[k] == dfn[k])
{
int t;
++scc;
do
{
t = s.top();s.pop();
v[t] = false;
ins[t] = scc;
}while(t != k);
}
return;
}
vector<int> g[MAXN << 1];
int ind[MAXN << 1];
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;++i)
{
scanf("%lld%lld",&x[i],&r[i]);
if(i == 1 || x[i] != x[i - 1])
{
p[x[i]] = ++tot;
to[tot] = x[i];
}
}
build(root,1,tot);
for(int i = 1;i <= n;++i)
{
int L = lower_bound(to + 1,to + 1 + tot,x[i] - r[i]) - to,R = upper_bound(to + 1,to + 1 + tot,x[i] + r[i]) - to - 1;
set(root,lef[p[x[i]]],L,R,1,tot);
}
memset(lbound,0x3f,sizeof(lbound));
memset(rbound,0xc0,sizeof(rbound));
for(int i = 1;i <= ptr;++i)
{
if(dfn[i] == 0)tarjan(i);
}
for(int k = 1;k <= ptr;++k)
{
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(ins[k] != ins[e[i].to])
{
g[ins[e[i].to]].push_back(ins[k]);
++ind[ins[k]];
}
}
}
queue<int> q;
for(int i = 1;i <= n;++i)
{
lbound[ins[lef[i]]] = min(lbound[ins[lef[i]]],x[i]);
rbound[ins[lef[i]]] = max(rbound[ins[lef[i]]],x[i]);
}
for(int i = 1;i <= scc;++i)
{
if(ind[i] == 0){q.push(i);}
}
while(!q.empty())
{
int k = q.front();q.pop();
for(int i = 0;i < g[k].size();++i)
{
lbound[g[k][i]] = min(lbound[g[k][i]],lbound[k]);
rbound[g[k][i]] = max(rbound[g[k][i]],rbound[k]);
--ind[g[k][i]];
if(ind[g[k][i]] == 0)
{
q.push(g[k][i]);
}
}
}
ll ans = 0;
for(int i = 1;i <= n;++i)
{
ans += ((ll)((upper_bound(x + 1,x + 1 + n,rbound[ins[lef[i]]]) - x) - (lower_bound(x + 1,x + 1 + n,lbound[ins[lef[i]]]) - x)) * (ll)i) % MOD;
ans %= MOD;
}
cout << ans << endl;
return 0;
}
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡