[BZOJ5017] [SNOI2017] 炸弹

题目链接

BZOJ:https://lydsy.com/JudgeOnline/problem.php?id=5017.

LOJ:https://loj.ac/problem/2255.

洛谷:https://www.luogu.org/problemnew/show/P5025.

Solution

这题有一个比较显然的暴力:

  • 对于一个点(x),我们二分出(l,r)表示(x)爆炸可以波及到的点的范围,然后我们从(x)([l,r])分别连边。

  • 建完图之后我们(tarjan)缩点,然后对于点(x)我们记录(L_x,R_x)表示(x)爆炸考虑连锁反应可以炸到的范围,这个在(DAG)(dp)就可以了,这个点的答案就是(R_x-L_x+1)

那么正解其实就很显然了,由于每次建边都是从一个点连向一个区间,那么直接线段树优化建图就好了。

具体的,我们建一棵(1sim n)的线段树,父亲向儿子连有向边,然后对于(x o [l,r]),我们把([l,r])拆成(O(log n))个区间,然后我们就只需要建(O(log n))条边了,然后缩点就直接在这个线段树上跑就好了。

#include<bits/stdc++.h>
using namespace std;

#define ll long long 

void read(ll &x) {
    x=0;ll f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('
');}

#define lf double
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)

const int maxn = 5e5+10;
const int inf = 1e9;
const lf eps = 1e-8;
const int mod = 1e9+7;

int col,n,bel[maxn<<2],rev[maxn<<2];

struct Graph {
	int head[maxn<<2],tot,deg[maxn<<2],l[maxn<<2],r[maxn<<2],t[maxn<<2],top;
	struct edge{int to,nxt;}e[maxn<<3];

	void ins(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot,deg[v]++;}
	
	void toposort() {
		queue<int > q;
		for(int i=1;i<=col;i++) if(!deg[i]) q.push(i);
		while(!q.empty()) {
			int now=q.front();q.pop();t[++top]=now;
			for(int i=head[now];i;i=e[i].nxt)
				if(!--deg[e[i].to]) q.push(e[i].to);
		}
		reverse(t+1,t+top+1);
		for(int x=1;x<=top;x++) {
			for(int i=head[x];i;i=e[i].nxt)
				l[x]=min(l[x],l[e[i].to]),r[x]=max(r[x],r[e[i].to]);
		}
	}
	
	void solve() {
		int ans=0;
		for(int i=1;i<=n;i++)
			ans=(ans+1ll*i*(r[bel[rev[i]]]-l[bel[rev[i]]]+1)%mod)%mod;
		write(ans);
	}
}G;

ll pos[maxn],R[maxn];
int head[maxn<<2],tot,dfn[maxn<<2],low[maxn<<2],dfn_cnt,sta[maxn<<2],top,vis[maxn<<2],id[maxn<<2];
struct edge{int to,nxt;}e[maxn<<4];

void ins(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot;}

void build(int p,int l,int r) {
	if(l==r) return rev[l]=p,id[p]=l,void();
	ins(p,ls),ins(p,rs);
	build(ls,l,mid),build(rs,mid+1,r);
}

void insert(int p,int l,int r,int x,int y,int t) {
	if(x<=l&&r<=y) return ins(t,p),void();
	if(x<=mid) insert(ls,l,mid,x,y,t);
	if(y>mid) insert(rs,mid+1,r,x,y,t);
}

void tarjan(int x) {
	dfn[x]=low[x]=++dfn_cnt,sta[++top]=x,vis[x]=1;
	for(int v,i=head[x];i;i=e[i].nxt)
		if(!dfn[v=e[i].to]) tarjan(v),low[x]=min(low[x],low[v]);
		else if(vis[v]) low[x]=min(low[x],dfn[v]);
	if(low[x]==dfn[x]) {
		col++;int r;G.l[col]=1e9;
		while(sta[top+1]!=x) {
			bel[r=sta[top--]]=col,vis[r]=0;
			if(id[r]) {
				G.l[col]=min(G.l[col],id[r]);
				G.r[col]=max(G.r[col],id[r]);
			}
		}
	}
}

int main() {
    scanf("%d",&n);build(1,1,n);
	for(int i=1;i<=n;i++) read(pos[i]),read(R[i]);
	for(int i=1;i<=n;i++) {
		int l=lower_bound(pos+1,pos+n+1,pos[i]-R[i])-pos;
		int r=upper_bound(pos+1,pos+n+1,pos[i]+R[i])-pos-1;
		insert(1,1,n,l,r,rev[i]);
	}
	for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
	for(int x=1;x<=n;x++)
		for(int i=head[x];i;i=e[i].nxt)
			if(bel[e[i].to]!=bel[x]) G.ins(bel[x],bel[e[i].to]);
	G.toposort(),G.solve();
	return 0;
}
原文地址:https://www.cnblogs.com/hbyer/p/10641317.html