zoukankan      html  css  js  c++  java
  • 【LOJ#2255】炸弹

    题目

    题目链接:https://loj.ac/problem/2255
    在一条直线上有 \(n\) 个炸弹,每个炸弹的坐标是 \(x_i\),爆炸半径是 \(r_i\),当一个炸弹爆炸时,如果另一个炸弹所在位置 \(x_j\) 满足 \(|x_j-x_i|\le r_i\) ,那么,该炸弹也会被引爆。
    现在,请你帮忙计算一下,先把第 \(i\) 个炸弹引爆,将引爆多少个炸弹呢?
    答案对 \(10^9 + 7\) 取模。

    思路

    首先显然每个炸弹炸的范围是一个区间。我们可以先求出这个区间 \([p_i,q_i]\)
    然后如果我们将 \(i\) 向满足 \(x\in [p_i,i)∪(i,q_i]\),那么就将 \(i\)\(x\) 连边。然后在这张图中,每个点能到达的点就是能炸到的炸弹。
    然而这种算法时空复杂度都不够优秀。考虑优化。
    首先解决空间复杂度,即建图的问题。普通的建图最高复杂度会达到 \(O(n^2)\)。但是由于每一个炸弹炸到的范围是一个区间,我们可以用线段树优化建图。线段树的每个子节点向父亲连边,然后对于每一个点 \(i\),将 \([p_i,i)\)\((i,q_i]\) 的在线段树中的区间连向 \(i\)。这样每个点最多连 \(\log n\) 条边,空间复杂度 \(O(n\log n)\)
    接下来考虑如何求每个点能到达哪些点。显然的,先将图缩点,变成一张 DAG。由于每一个炸弹爆炸到的点是一个范围,所以可以直接维护每一个 SCC 能爆炸到的最小/最大编号的炸弹,那么这个 SCC 能炸到的点数量即为两者的差加一。
    接下来处理 DAG 中的边。显然可以用 topsort 来解决不是一个 SCC 内的点的爆炸关系。具体的,如果在 DAG 中有一条边 \((x,y)\),那么就让 \(y\) 所能爆炸到的最值与 \(x\) 所能爆炸到的最值取 \(\operatorname{min/max}\)
    那么最终假设点 \(i\) 处于第 \(col[i]\) 个 SCC,那么点 \(i\) 做出的贡献即为 \(i\times (R[col[i]]-L[col[i]]+1)\)。其中 \(L,R\) 分别为这个 SCC 所能爆炸到的点的编号范围。
    时间复杂度 \(O(n\log n+n)\)。在洛谷上吸氧能过,LOJ 上直接过了。

    代码

    #include <stack>
    #include <queue>
    #include <cctype>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define reg register
    using namespace std;
    typedef long long ll;
    
    const int N=2000010,M=500010*30,MOD=1e9+7; 
    int head[M],From[M],To[M],id[N],dfn[N],low[N],col[N],p[N],q[N],deg[N],L[N],R[N];
    int n,tot,Maxn,cnt;
    ll ans,pos[N],len[N];
    bool vis[N]; 
    stack<int> st;
    
    struct edge
    {
    	int next,to;
    }e[M];
    
    inline ll read()
    {
    	ll d=0,f=1; char ch=getchar();
    	while (!isdigit(ch)) f=(ch=='-'?-1:f),ch=getchar();
    	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
    	return d*f; 
    }
    
    void add(int from,int to)
    {
    	e[++tot].to=to;
    	e[tot].next=head[from];
    	head[from]=tot;
    	From[tot]=from; To[tot]=to;
    }
    
    struct SegTree
    {
    	int l[M],r[M];
    	
    	void build(int x,int ql,int qr)
    	{
    		l[x]=ql; r[x]=qr;
    		Maxn=max(Maxn,x);
    		if (ql==qr)
    		{
    			id[ql]=x;
    			return;
    		}
    		int mid=(ql+qr)>>1;
    		build(x*2,ql,mid);
    		build(x*2+1,mid+1,qr);
    		add(x*2,x); add(x*2+1,x);
    	}
    	
    	void update(int x,int ql,int qr,int i)
    	{
    		if (ql>qr) return;
    		if (ql==l[x] && qr==r[x])
    		{
    			add(x,id[i]);
    			return;
    		}
    		int mid=(l[x]+r[x])>>1;
    		if (qr<=mid) update(x*2,ql,qr,i);
    		else if (ql>mid) update(x*2+1,ql,qr,i);
    		else update(x*2,ql,mid,i),update(x*2+1,mid+1,qr,i);
    	}
    }seg;
    
    void tarjan(int x)
    {
    	dfn[x]=low[x]=++tot;
    	st.push(x); vis[x]=1;
    	for(reg int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (!dfn[v])
    		{
    			tarjan(v);
    			low[x]=min(low[x],low[v]);
    		}
    		else if (vis[v])
    			low[x]=min(low[x],dfn[v]);
    	}
    	if (dfn[x]==low[x])
    	{
    		int y; cnt++;
    		do {
    			y=st.top(); st.pop();
    			vis[y]=0; col[y]=cnt;
    			L[cnt]=min(L[cnt],p[y]);
    			R[cnt]=max(R[cnt],q[y]);
    		} while (x!=y); 
    	}
    }
    
    inline void topsort()
    {
    	queue<int> Q;
    	for(reg int i=1;i<=cnt;i++)
    		if (!deg[i]) Q.push(i);
    	while (Q.size())
    	{
    		int u=Q.front(); Q.pop();
    		for(reg int i=head[u];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			L[v]=min(L[v],L[u]);
    			R[v]=max(R[v],R[u]);
    			deg[v]--;
    			if (!deg[v]) Q.push(v); 
    		}
    	}
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	memset(p,0x3f3f3f3f,sizeof(p));
    	memset(L,0x3f3f3f3f,sizeof(L));
    	n=read();
    	seg.build(1,1,n);
    	for(reg int i=1;i<=n;i++)
    		pos[i]=read(),len[i]=read();
    	pos[0]=-9223372036854775808LL;
    	pos[n+1]=9223372036854775807LL;
    	for(reg int i=1;i<=n;i++)
    	{
    		p[id[i]]=lower_bound(pos,pos+n+2,pos[i]-len[i])-pos;
    		q[id[i]]=upper_bound(pos,pos+n+2,pos[i]+len[i])-pos-1;
    		seg.update(1,p[id[i]],i-1,i);
    		seg.update(1,i+1,q[id[i]],i);
    	}
    	int tot2=tot;
    	tot=0;
    	for(reg int i=1;i<=Maxn;i++)	
    		if (!dfn[i]) tarjan(i);
    	memset(head,-1,sizeof(head));
    	tot=0;
    	for(reg int i=1;i<=tot2;i++)
    	{
    		int x=col[From[i]],y=col[To[i]];
    		if (x!=y)
    		{
    			add(x,y);
    			deg[y]++;
    		}
    	}
    	topsort();
    	for(reg int i=1;i<=n;i++)
    		ans=(ans+1LL*i*(R[col[id[i]]]-L[col[id[i]]]+1))%MOD;
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    rest framework 认证 权限 频率
    rest framework 视图,路由
    rest framework 序列化
    10.3 Vue 路由系统
    10.4 Vue 父子传值
    10.2 Vue 环境安装
    10.1 ES6 的新增特性以及简单语法
    Django 跨域请求处理
    20190827 On Java8 第十四章 流式编程
    20190825 On Java8 第十三章 函数式编程
  • 原文地址:https://www.cnblogs.com/stoorz/p/13137846.html
Copyright © 2011-2022 走看看