1. 程式人生 > >BSOJ 5603 -- 【SNOI2017】炸彈

BSOJ 5603 -- 【SNOI2017】炸彈

題好資料水系列,網上的十幾行神仙解法A了原資料。

這道題要用到線段數優化建圖的知識。然而考試考到這道題時我還不會。

我們設lx[i],rx[i]分別表示每個炸彈向左和向右最遠能炸到哪個炸彈。很容易想到一個思路,就是每個炸彈都連邊連向它能直接炸的所有炸彈,然後tarjan縮塊後DAG圖上DP。如果a到b有邊,那麼就能用b的lx[b],rx[b]更新lx[a],rx[a]。但是直接連邊顯然是不行的。

我們仔細思考一下就會發現,一個炸彈能直接炸炸彈一定是一段連續的區間,這就給了我們想象的空間。我們就建一顆區間線段樹,每個點的代表元就是對應的葉子節點。我們將所有節點(除了葉子節點)連向兩個兒子節點。然後考慮一個炸彈i,假設它能直接引爆的區間是[l,r],那我們就用類似於線段樹區間修改的方法將遞迴過程中遇到的包含於[l,r]

的區間與i的代表元連邊。然後就是tarjan的事了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<ctime>
#define ll long long
#define N 2000005

using namespace std;
inline ll Get() {ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

const ll mod=1e9+7;
struct tree {
	int l,r;
	int lx,rx;
}tr[N];
struct load {int to,next;}s[N*15];
int h[N],cnt;
void add(int i,int j) {
	s[++cnt]=(load) {j,h[i]};h[i]=cnt;
}
int tot;
void Link(int fr,int v,int l,int r) {
	if(tr[v].l>r||tr[v].r<l) return ;
	if(l<=tr[v].l&&tr[v].r<=r) {
		if(fr!=v) add(fr,v);
		return ;
	}
	Link(fr,v<<1,l,r);
	Link(fr,v<<1|1,l,r);
}
int pos[N];
void build(int v,int l,int r) {
	tr[v].lx=tr[v].l=l;
	tr[v].rx=tr[v].r=r;
	tot=max(tot,v);
	if(l==r) return pos[l]=v,void();
	int mid=l+r>>1;
	build(v<<1,l,mid),build(v<<1|1,mid+1,r);
	add(v,v<<1),add(v,v<<1|1);
}
int n;
ll x[N],r[N];
vector<ll>p;
int low[N],dfn[N],id;
int st[N],bel[N],scc;
int lx[N],rx[N];
bool ins[N];
void tarjan(int v) {
	low[v]=dfn[v]=++id;
	st[++st[0]]=v;
	ins[v]=1;
	for(int i=h[v];i;i=s[i].next) {
		int to=s[i].to;
		if(!dfn[to]) {
			tarjan(to);
			low[v]=min(low[v],low[to]);
		} else if(ins[to]) low[v]=min(low[v],dfn[to]);
	}
	if(low[v]==dfn[v]) {
		scc++;
		while(1) {
			int j=st[st[0]--];
			ins[j]=0;
			bel[j]=scc;
			if(j==v) break;
		}
	}
}
vector<int>e[N];
int d[N];
bool vis[N];
void dfs(int v) {
	vis[v]=1;
	for(int i=0,s=e[v].size();i<s;i++) {
		int to=e[v][i];
		if(!vis[to]) dfs(to);
		lx[v]=min(lx[v],lx[to]);
		rx[v]=max(rx[v],rx[to]);
	}
}
ll ans;
int main() {
	int size=40<<20;//40M
	__asm__ ("movq %0,%%rsp\n"::"r"((char*)malloc(size)+size));
	n=Get();
	build(1,1,n);
	for(int i=1;i<=n;i++) {
		x[i]=Get(),r[i]=Get();
		p.push_back(x[i]);
	}
	for(int i=1;i<=n;i++) {
		int Lx=lower_bound(p.begin(),p.end(),x[i]-r[i])-p.begin();
		int Rx=upper_bound(p.begin(),p.end(),x[i]+r[i])-p.begin()-1;
		lx[i]=Lx+1,rx[i]=Rx+1;
		Link(pos[i],1,lx[i],rx[i]);
	}
	for(int i=1;i<=tot;i++) {
		if(!dfn[i]) tarjan(i);
	}
	
	for(int i=1;i<=scc;i++) lx[i]=n+1,rx[i]=0;
	for(int i=1;i<=tot;i++) {
		lx[bel[i]]=min(lx[bel[i]],tr[i].lx);
		rx[bel[i]]=max(rx[bel[i]],tr[i].rx);
		for(int j=h[i];j;j=s[j].next) {
			int to=s[j].to;
			if(bel[i]!=bel[to]) {
				e[bel[i]].push_back(bel[to]);
				d[bel[to]]++;
			}
		}
	}
	for(int i=1;i<=scc;i++)
		if(!d[i]) dfs(i);
	for(int i=1;i<=n;i++) {
		ans=(ans+i*(rx[bel[pos[i]]]-lx[bel[pos[i]]]+1+mod)%mod)%mod;
	}
	cout<<ans;
	exit(0);
	return 0;
}