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;
    }
    
  • 相关阅读:
    大小端模式
    深入理解c/c++ 内存对齐
    示波器使用
    C结构体
    51单片机内存问题
    S5PV210启动过程详解1
    程序中内存从哪里来
    再论typedef
    ARM体系结构总结
    MMU实验实验
  • 原文地址:https://www.cnblogs.com/stoorz/p/13137846.html
Copyright © 2011-2022 走看看