首页 > 其他分享 >炸弹题解

炸弹题解

时间:2024-03-15 17:34:17浏览次数:18  
标签:rt idx int 题解 long vis 炸弹 low

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;
}

标签:rt,idx,int,题解,long,vis,炸弹,low
From: https://www.cnblogs.com/wlesq/p/18074111

相关文章

  • 【PR #12】划分序列 / Yet Another Mex Problem 题解
    题目链接题目大意给定一个长度为\(n\)的序列\(a\),定义一段区间的价值为该区间的\(\operatorname{mex}\)乘上区间元素总和。你需要将序列划分成若干个长度\(\leqk\)的区间。一个划分方案的价值为划分出来的每个区间价值之和,求所有划分方案的价值最大值。\(1\leqk\le......
  • centos6使用yum网络源失败,问题解决
    在进行测试环境部署时,需要用到yum安装一些软件包,目前服务器是通外网的,所以这里我就直接使用的网络源进行yum下载的令我惊讶的是用yum命令安装居然失败了!!!以下是我的排查到解决的心路历程:1.首先执行命令yumlist查看发现报错如下:从报错信息来看是说无法连接到http(s),ftp的......
  • CF575H Bots 题解 组合数学
    Bots传送门SashaandIraaretwobestfriends.Buttheyaren’tjustfriends,theyaresoftwareengineersandexpertsinartificialintelligence.Theyaredevelopinganalgorithmfortwobotsplayingatwo-playergame.Thegameiscooperativeandturn......
  • 常见问题解决 --- idea与maven使用常识
    1.拿到项目代码后先要知道使用了哪些技术和工具。比如使用的是idea、eclipse还是maven创建的项目,使用什么编程语言,使用什么项目目录结构等等2.如何用maven创建的项目必然有pom.xml,每次修改pom文件后必须重新加载。3.如果修改代码后还是报错,尝试使用clean清除编译缓存再同步maven......
  • Luna likes Love 题解
    ProblemLink简要题意题目很清楚。分析定理两个人中左边的人一直向右运动,和两人向中间走所用的步数相同证明假设有两组人为\(a_l,a_r,b_l,b_r(a_l<a_r,b_l<b_r)\)。\(\textrm{I}.\)当\(a_r<b_l\)(两者互不相交)时如图:显然成立。$\textrm{II}.$......
  • abc155F题解
    abc155F题意:给定\(n\)个灯泡的位置\(a_i\)和状态\(b_i(0/1)\)。给定\(m\)个开关控制区间\([l_i,r_i]\)中所有的灯泡,即使用这个开关会使\([l_i,r_i]\)中所有的灯泡的状态都取反。问能否使这\(n\)个灯泡的状态都变成\(0\),如果可以,输出一种方案,否则,输出\(-1\)。思路:神仙转化题。......
  • CF436E - Cardboard Box 题解
    只讲贪心做法。一、反悔贪心考虑如何使选的星星总数多一。显然,有如下几种方式:选一个之前没选过的位置\(i\),答案加上\(a_i\)。选一个之前选过一次的位置\(i\),答案加上\(b_i-a_i\)。对于一个之前选过一次的位置\(i\),再找到一个没有选过的位置\(j\),反悔掉\(i\),并选......
  • 洛谷 P3596 [POI2015] MOD 题解
    题意简述给定一棵树,求断掉一条边再连上一条边所得的新树直径最小值和最大值,以及相应方案(你可以不进行任何操作,即断掉并连上同一条边)。题目分析假设我们枚举断掉某一条边,得到了两棵树,并且知道它们的直径分别为\(d_0,d_1\),那么如何连接一条边让新树的直径最大/最小呢?最大:显......
  • 洛谷 P3261 [JLOI2015] 城池攻占 题解
    题目分析其他人要么倍增,要么左偏树,那我就来讲讲朴实无华的dfs序加上线段树的做法。首先发现题目中明确指出了作乘法的时候一定是乘上一个大于零的数,这是为什么呢?首先把可以占领当前城池的战斗力的不等式列出来:\[h_j\le\left\{\begin{array}{c}s_i\timesv_j&&{a_j=......
  • 「PKUSC2022」雀圣 题解
    这边电脑的输入法要把我干烧了。。D2T3出这种模拟题,那简直不要太好。首先,不难想到暴力搜索要摸那些牌,然后丢哪些牌。然后手搓一些\(\texttt{check}\),这个应该都会,但是实际上比正解难写。我不知道\(\texttt{lay}\)打的什么东西,比我快20多倍,但是个人认为我这个思路挺暴力的。......