P5025-[SNOI2017]炸弹【tarjan,线段树优化建图】

正题

题目链接:https://www.luogu.com.cn/problem/P5025


题目大意

. n n n个炸弹,每个在 x x x位置处,范围为 r r r。定义 f i f_i fi表示第 i i i个炸弹爆炸能连锁的炸弹数,要求输出 ∑ i = 1 n f i ∗ i \sum_{i=1}^nf_i*i i=1nfii


解题思路

将每个炸弹可以炸开的炸弹连边,然后每个强连通分量之间可以互相炸,然后在 t a r j a n tarjan tarjan时统计这个强连通分量内的炸弹炸的范围 l i , r i l_i,r_i li,ri。然后用 d f s dfs dfs D A G DAG DAG上更新即可。

但是这样建的边是 n 2 n^2 n2级别的,显然不能通过本题,因为建的边都是在一个区间内,用线段树优化建边,每个节点代表这个这个节点能够通向连向的区间点,然后优化为 n log ⁡ n n\log n nlogn的级别。


c o d e code code

#include
#include
#include
#include
#define ll long long
using namespace std;
const ll N=2e6+10,XJQ=1e9+7;
struct node{
	ll to,from,next;
}a[N*15];
ll n,tot,num,cnt,ans;
ll lso[N],rso[N],dfn[N],low[N];
ll l[N],r[N],ls[N],fa[N],w[N],rr[N];
stack<int> S;
bool v[N];
void addl(ll x,ll y){
	a[++tot].to=y;
	a[tot].from=x;
	a[tot].next=ls[x];
	ls[x]=tot;
	return;
}
ll build(ll x,ll l,ll r){
	if(l==r)return l;
	if(!x)x=++cnt;
	ll mid=(l+r)>>1;
	lso[x]=build(lso[x],l,mid);
	rso[x]=build(rso[x],mid+1,r);
	addl(x,lso[x]);addl(x,rso[x]);
	return x;
}
void change(ll x,ll L,ll R,ll l,ll r,ll pos){
	if(L==l&&R==r)
		{addl(pos,x);return;}
	ll mid=(L+R)>>1;
	if(r<=mid)change(lso[x],L,mid,l,r,pos);
	else if(l>mid)change(rso[x],mid+1,R,l,r,pos);
	else change(lso[x],L,mid,l,mid,pos),
		 change(rso[x],mid+1,R,mid+1,r,pos);
	return;
}
void tarjan(ll x){
	dfn[x]=low[x]=++num;
	v[x]=1;S.push(x);
	for(ll i=ls[x];i;i=a[i].next){
		ll y=a[i].to;
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(v[y]) 
			low[x]=min(low[x],dfn[y]);
	}
	if(dfn[x]==low[x]){
		ll y;
		do{
			y=S.top();
			v[y]=0;fa[y]=x;
			l[x]=min(l[x],l[y]);
			r[x]=max(r[x],r[y]);
			S.pop();
		}while(x!=y);
	}
	return;
}
void dfs(ll x){
	v[x]=1;
	for(ll i=ls[x];i;i=a[i].next){
		ll y=a[i].to;
		if(!v[y])dfs(y);
		l[x]=min(l[x],l[y]);
		r[x]=max(r[x],r[y]);
	}
	return;
}
int main()
{
	scanf("%lld",&n);
	for(ll i=1;i<=n;i++)
		scanf("%lld%lld",&w[i],&rr[i]);
	cnt=n;build(0,1,n);
	memset(l,127,sizeof(l));
	w[n+1]=2147483647;
	for(ll i=1;i<=n;i++){
		ll L=lower_bound(w+1,w+1+n,w[i]-rr[i])-w;
		ll R=upper_bound(w+1,w+1+n,w[i]+rr[i])-w-1;
		change(n+1,1,n,L,R,i);l[i]=L;r[i]=R;
	}
	for(ll i=1;i<=cnt;i++)
		if(!dfn[i])tarjan(i);
	ll TOT=tot;tot=0;
	memset(ls,0,sizeof(ls));
	for(ll i=1;i<=TOT;i++){
		ll x=a[i].from,y=a[i].to;
		if(fa[x]==fa[y])continue;
		addl(fa[a[i].from],fa[a[i].to]);
	}
	memset(v,0,sizeof(v));
	for(ll i=1;i<=cnt;i++)
		if(fa[i]==i)dfs(i);
	for(ll i=1;i<=n;i++)
		ans=(ans+(r[fa[i]]-l[fa[i]]+1)*i%XJQ)%XJQ;
	printf("%lld",ans);
	return 0;
}

你可能感兴趣的:(数据结构,图论,luogu,SNOI,tarjan,线段树)