返回顶部

炸弹题解

image
这题有两种做法,一种tarjan,一种逆天DP
用lower_bound或upper查找i所在范围的左右边界对应下标

普通Tarjan+缩点
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+10,mod=1e9+7;
int n,dfn[N],low[N],head2[N],num,cnt,head[N],belong[N];bool vis[N];
int x[N],R[N];
struct ac
{
	int l=LONG_LONG_MAX,r=0,id;
//	bool operator <(const ac &A)const
//	{
//		return x<A.x;
//	}
}a[N],gd[N];
struct Edge
{
	int to,next,from,id;
}edge[N*4],edge2[N*3];
int ge;
void add(int u,int v)
{
	edge[++cnt].to=v;
	edge[cnt].next=head[u];
	edge[cnt].from=u;
	head[u]=cnt;
}
int cnt2;
void add2(int u,int v)
{
	edge2[++cnt2].to=v;
	edge2[cnt2].next=head2[u];
	edge2[cnt2].from=u;
	head2[u]=cnt2;
}
stack <int> stk;
void tarjan(int now)
{
//	cout<<1;
	dfn[now]=low[now]=++num;
	vis[now]=1;
	stk.push(now);
	for(int i=head[now];i;i=edge[i].next)
	{
		int to=edge[i].to;
		if(!dfn[to])
		{
			tarjan(to);
			low[now]=min(low[to],low[now]);
			a[now].l=min(a[to].l,a[now].l);
			a[now].r=max(a[to].r,a[now].r);
		}else if(vis[to])
		{
			low[now]=min(dfn[to],low[now]);
			a[now].l=min(a[to].l,a[now].l);
			a[now].r=max(a[to].r,a[now].r);
		}
	}
	if(low[now]==dfn[now])
	{
		int x;
		ge++;
		do
		{
			x=stk.top();
			stk.pop();
			vis[x]=0;
			belong[x]=ge;
			gd[ge].l=min(a[x].l,gd[ge].l);
			gd[ge].r=max(a[x].r,gd[ge].r);
		}while(now!=x);
		
	}
}
bool flag[N];
int findl(int i,int val)
{
	return lower_bound(x+1,x+1+n,val)-x;
}
int findr(int i,int val)
{
//	return upper_bound(x+1,x+1+n,val)-x-1;
	int s=lower_bound(x+1,x+1+n,val)-x;
	if(x[s]!=val)
	{
		s--;
	}
	return s;
}
void dfs(int now)
{
	flag[now]=1;
	for(int i=head2[now];i;i=edge2[i].next)
	{
		int to=edge2[i].to;
//		cout<<now<<" "<<to<<endl;
		if(!flag[to])dfs(to);
		gd[now].l=min(gd[to].l,gd[now].l);
		gd[now].r=max(gd[to].r,gd[now].r);
//		cout<<gd[now].l<<" "<<gd[now].r<<endl;
	}
}

signed main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>x[i]>>R[i];
		a[i].id=i;
	}
	for(int i=1;i<=n;i++)
	{
		a[i].l=findl(i,x[i]-R[i]);
		a[i].r=findr(i,x[i]+R[i]);		
	}//这里要提前全部更新出来,不能套在下面循环中,MD调了半天
	for(int i=1;i<=n;i++)
	{
		for(int j=a[i].l;j<=a[i].r;j++)
		{	
			if(i==j)continue;
			add(i,j);
			j=a[j].r;//防止j一个一个加炸内存,很巧妙,i连j以后,只用考虑j右边界以外的情况,因为i循环到j时会连j内的情况
		}		
	}
	for(int i=1;i<=n;i++)
	{
		if(!dfn[i])tarjan(i);
	}
//	for(int i=1;i<=n;i++)cout<<belong[i]<<" ";
//	for(int i=1;i<=n;i++)
//	{
		for(int j=1;j<=cnt;j++)
//		for(int j=head[i];j;j=edge[j].next)
		{
			if(belong[edge[j].from]!=belong[edge[j].to])
			{
//				cout<<belong[edge[j].from]<<" "<<belong[edge[j].to]<<endl;
				add2(belong[edge[j].from],belong[edge[j].to]);
			}
		}
//	}

	long long ans=0;
	for(int i=1;i<=ge;i++)if(!flag[i])dfs(i);
	for(int i=1;i<=n;i++)
	{
//		cout<<gd[belong[i]].l<<" "<<gd[belong[i]].r<<endl;
		ans=(ans+(gd[belong[i]].r-gd[belong[i]].l+1)*a[i].id%mod)%mod;
	}
	cout<<ans;
	return 0;
}
/*
4
1 1
5 1
6 5
15 15

*/
线段树优化
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define mid ((l + r) >> 1)
#define lid rt<<1
#define rid (rt<<1|1)
const int N = 5e5+10, M=2e6+10, mod =1e9 + 7; 
struct node {
	int l,r;
}a[M];
ll x[N], r[N];
int dfn[M], low[M], bel[M];
int id[M], Left[M], Right[M];
int n, nd, cnt, idx;ll ans;
stack<int> s;
vector<int> e[M], G[M];
bool vis[M];
void bt(int rt,int l,int r)
{
	a[rt].l=l;a[rt].r=r;
	nd=max(nd,rt);
	if(l==r)
	{
		id[l]=rt;
		return;
	}
	bt(lid,l,mid);
	bt(rid,mid+1,r);
	e[rt].push_back(lid);e[rt].push_back(rid);
}
void update(int rt,int l,int r,int L,int R,int id)
{
	if(L<=l&&r<=R)
	{
		if(rt==id)return;
		e[id].push_back(rt);
		return;
	}
	if(L<=mid)update(lid,l,mid,L,R,id);
	if(R>mid)update(rid,mid+1,r,L,R,id);
}
void tarjan(int x) {
	dfn[x] = low[x] = ++ cnt;
	s.push(x), vis[x] = 1;
	for (int i = 0; i < e[x].size(); i ++) {
		int y = e[x][i];
		if (!dfn[y]) {
			tarjan(y);
			low[x] = min(low[x], low[y]);
		}
		else if (vis[y]) low[x] = min(low[x], dfn[y]);
	}
	if (dfn[x] == low[x]) {
		idx ++;
		int v;
		do {
			v = s.top(); s.pop();
			vis[v] = 0;
			bel[v] = idx;
			Left[idx] = min(Left[idx], a[v].l);
			Right[idx] = max(Right[idx], a[v].r);
		} while (v != x);
	}
}
void dfs(int now)
{
	vis[now]=1;
	for(int i=0;i<G[now].size();i++)
	{
		int to=G[now][i];
		if (vis[to]) {
			Left[now] = min(Left[now], Left[to]);
			Right[now] = max(Right[now], Right[to]);
			continue;
		}
		dfs(to);
		Left[now] = min(Left[now], Left[to]);
		Right[now] = max(Right[now], Right[to]);
	}
}
ll query(int x)
{
	int u=bel[id[x]];
	return Right[u]-Left[u]+1;
}
int main()
{
	scanf("%d", &n);
	for(int i=1;i<=n;i++)scanf("%lld %lld", &x[i], &r[i]);
	memset(Left, 0x3f, sizeof Left);
	bt(1,1,n);
//	for(int i=1;i<=n;i++)cout<<id[i]<<" ";
	for(int i=1;i<=n;i++)
	{
		int L=lower_bound(x + 1, x + n + 1, x[i] - r[i]) - x;
		int R=upper_bound(x + 1, x + n + 1, x[i] + r[i]) - x - 1;
		update(1,1,n,L,R,id[i]);
		a[id[i]].l=L;a[id[i]].r=R;
	}
	tarjan(1);
	for (int i = 1; i <= nd; i ++) {
		for (int j = 0; j < e[i].size(); j ++) {
			int u = e[i][j];
			if (bel[u]!= bel[i])
				G[bel[i]].push_back(bel[u]);
		}
	}
	for(int i=1;i<=idx;i++)
	{
		sort(G[i].begin(),G[i].end());
		unique(G[i].begin(),G[i].end());
	}
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=idx;i++)
	{
		if(!vis[i])dfs(i);
	}
	for (int i=1;i<=n;i++) 
		ans = (ans + 1ll*query(i)*i)%mod;
	printf("%lld", ans);
	return 0;
}

逆天做法
一个DP,先从左往右一遍,在从右往左一遍,两个数组l,r就是左右边界

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const long long N=500005,mod=1e9+7;
int n;
struct ac
{
	int x,r,l,id;
	bool operator <(const ac &A)const
	{
		return x<A.x;
	}
}a[N];
int f[N],sum[N];
int l[N],r[N];int ans;
signed main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i].x>>a[i].r;
		l[i]=r[i]=i;
	}
//	sort(a+1,a+1+n);
	for(int i=1;i<=n;i++)
	{
		while(a[i].x-a[i].r<=a[l[i]-1].x&&l[i]>1)
		{
			a[i].r=max(a[i].r,a[l[i]-1].r-(a[i].x-a[l[i]-1].x));//更新l时更新一下R,意思为xi+ri->xj+rj
			l[i]=l[l[i]-1];
		}		
	}
	for(int i=n;i>=1;i--)
	{
		while(a[i].x+a[i].r>=a[r[i]+1].x&&r[i]<n)
		{
			
			l[i]=min(l[i],l[r[i]+1]);
			r[i]=r[r[i]+1];
		}		
	}
	
	for(int i=1;i<=n;i++)
	{
//		cout<<a[i].id<<" "<<f[i]<<" "<<endl;
		ans+=(r[i]-l[i]+1)*i;
	}
	cout<<(long long)ans%mod;
	return 0;
}
posted @ 2024-03-15 17:26  wlesq  阅读(12)  评论(0编辑  收藏  举报